@rudderhq/cli 0.7.6 → 0.7.7

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.js CHANGED
@@ -3460,17 +3460,20 @@ var init_resource = __esm({
3460
3460
  resourceId: z19.string().uuid(),
3461
3461
  role: projectResourceAttachmentRoleSchema.optional(),
3462
3462
  note: z19.string().optional().nullable(),
3463
- sortOrder: z19.number().int().nonnegative().optional()
3463
+ sortOrder: z19.number().int().nonnegative().optional(),
3464
+ isPrimary: z19.boolean().optional()
3464
3465
  }).strict();
3465
3466
  updateProjectResourceAttachmentSchema = z19.object({
3466
3467
  role: projectResourceAttachmentRoleSchema.optional(),
3467
3468
  note: z19.string().optional().nullable(),
3468
- sortOrder: z19.number().int().nonnegative().optional()
3469
+ sortOrder: z19.number().int().nonnegative().optional(),
3470
+ isPrimary: z19.boolean().optional()
3469
3471
  }).strict();
3470
3472
  createProjectInlineResourceSchema = createOrganizationResourceBaseSchema.extend({
3471
3473
  role: projectResourceAttachmentRoleSchema.optional(),
3472
3474
  note: z19.string().optional().nullable(),
3473
- sortOrder: z19.number().int().nonnegative().optional()
3475
+ sortOrder: z19.number().int().nonnegative().optional(),
3476
+ isPrimary: z19.boolean().optional()
3474
3477
  }).strict().superRefine(validateLibraryResourceContract);
3475
3478
  }
3476
3479
  });
@@ -5413,9 +5416,13 @@ var init_postgres_payload = __esm({
5413
5416
 
5414
5417
  // src/runtime/install.ts
5415
5418
  import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
5419
+ import { createHash as createHash2 } from "node:crypto";
5420
+ import { createReadStream, createWriteStream } from "node:fs";
5416
5421
  import { copyFile, mkdir as mkdir2, mkdtemp, readFile, readdir, realpath, rename, rm, stat as stat2, symlink, writeFile } from "node:fs/promises";
5417
5422
  import { createRequire } from "node:module";
5418
5423
  import path7 from "node:path";
5424
+ import { Readable, Transform } from "node:stream";
5425
+ import { pipeline } from "node:stream/promises";
5419
5426
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
5420
5427
  function sanitizeRuntimeCacheSegment(value) {
5421
5428
  return encodeURIComponent(value.trim() || "latest").replaceAll("%", "_");
@@ -6137,8 +6144,29 @@ function runtimePostgresArchiveUrl(platform = process.platform, arch = process.a
6137
6144
  return null;
6138
6145
  }
6139
6146
  async function downloadRuntimePostgresArchive(url, targetPath) {
6147
+ const expectedSha256 = process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV]?.trim().toLowerCase() || null;
6148
+ if (expectedSha256 && !/^[a-f0-9]{64}$/.test(expectedSha256)) {
6149
+ throw new Error(`${RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV} must be a 64-character SHA-256 digest`);
6150
+ }
6151
+ const configuredMaxBytes = Number.parseInt(process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV] ?? "", 10);
6152
+ const maxBytes = Number.isSafeInteger(configuredMaxBytes) && configuredMaxBytes > 0 ? configuredMaxBytes : DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES;
6153
+ async function verifyFile(filePath) {
6154
+ if (!expectedSha256) return;
6155
+ const hash = createHash2("sha256");
6156
+ await new Promise((resolve, reject) => {
6157
+ const stream = createReadStream(filePath);
6158
+ stream.on("data", (chunk) => hash.update(chunk));
6159
+ stream.on("error", reject);
6160
+ stream.on("end", resolve);
6161
+ });
6162
+ const actual = hash.digest("hex");
6163
+ if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
6164
+ }
6140
6165
  if (url.startsWith("file://")) {
6141
6166
  await copyFile(fileURLToPath3(url), targetPath);
6167
+ const archiveStat = await stat2(targetPath);
6168
+ if (archiveStat.size > maxBytes) throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
6169
+ await verifyFile(targetPath);
6142
6170
  return;
6143
6171
  }
6144
6172
  const parsedTimeout = Number.parseInt(
@@ -6152,7 +6180,33 @@ async function downloadRuntimePostgresArchive(url, targetPath) {
6152
6180
  if (!response.ok) {
6153
6181
  throw new Error(`failed to download ${url}: ${response.status} ${response.statusText}`);
6154
6182
  }
6155
- await writeFile(targetPath, Buffer.from(await response.arrayBuffer()));
6183
+ const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
6184
+ if (Number.isSafeInteger(contentLength) && contentLength > maxBytes) {
6185
+ throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
6186
+ }
6187
+ if (!response.body) throw new Error("PostgreSQL runtime archive response has no body");
6188
+ const hash = createHash2("sha256");
6189
+ let bytes = 0;
6190
+ const monitor = new Transform({
6191
+ transform(chunk, _encoding, callback) {
6192
+ bytes += chunk.byteLength;
6193
+ if (bytes > maxBytes) {
6194
+ callback(new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`));
6195
+ return;
6196
+ }
6197
+ hash.update(chunk);
6198
+ callback(null, chunk);
6199
+ }
6200
+ });
6201
+ await pipeline(
6202
+ Readable.fromWeb(response.body),
6203
+ monitor,
6204
+ createWriteStream(targetPath, { flags: "wx" })
6205
+ );
6206
+ if (expectedSha256) {
6207
+ const actual = hash.digest("hex");
6208
+ if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
6209
+ }
6156
6210
  } finally {
6157
6211
  if (timeout) clearTimeout(timeout);
6158
6212
  }
@@ -6672,7 +6726,7 @@ function parseRuntimeVersion(version) {
6672
6726
  canaryNumber: canaryMatch ? Number(canaryMatch[1]) : null
6673
6727
  };
6674
6728
  }
6675
- var RUNTIME_NPM_PACKAGE_NAME, NPM_PUBLIC_REGISTRY_URL, RUNTIME_METADATA_FILE, RUNTIME_POSTGRES_PAYLOAD_DIR, DEFAULT_RUNTIME_CACHE_MAX_ENTRIES, DEFAULT_RUNTIME_CACHE_MAX_AGE_MS, DEFAULT_RUNTIME_CACHE_MAX_BYTES, DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS, RUNTIME_NPM_INSTALL_FLAGS, RUNTIME_NPM_PACK_FLAGS, EMBEDDED_POSTGRES_PACKAGE_NAME, RUDDER_DESKTOP_MANAGED_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV, RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV, RUNTIME_CACHE_PACKAGE_JSON, NPM_PLATFORM_REPAIR_ENV, RuntimeInstallError;
6729
+ var RUNTIME_NPM_PACKAGE_NAME, NPM_PUBLIC_REGISTRY_URL, RUNTIME_METADATA_FILE, RUNTIME_POSTGRES_PAYLOAD_DIR, DEFAULT_RUNTIME_CACHE_MAX_ENTRIES, DEFAULT_RUNTIME_CACHE_MAX_AGE_MS, DEFAULT_RUNTIME_CACHE_MAX_BYTES, DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS, RUNTIME_NPM_INSTALL_FLAGS, RUNTIME_NPM_PACK_FLAGS, EMBEDDED_POSTGRES_PACKAGE_NAME, RUDDER_DESKTOP_MANAGED_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_BIN_DIR_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV, RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV, RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV, DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES, RUNTIME_CACHE_PACKAGE_JSON, NPM_PLATFORM_REPAIR_ENV, RuntimeInstallError;
6676
6730
  var init_install = __esm({
6677
6731
  "src/runtime/install.ts"() {
6678
6732
  "use strict";
@@ -6693,6 +6747,9 @@ var init_install = __esm({
6693
6747
  RUDDER_POSTGRES_BIN_DIR_ENV = "RUDDER_POSTGRES_BIN_DIR";
6694
6748
  RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_URL";
6695
6749
  RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV = "RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS";
6750
+ RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256";
6751
+ RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV = "RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES";
6752
+ DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES = 1024 * 1024 * 1024;
6696
6753
  RUNTIME_CACHE_PACKAGE_JSON = {
6697
6754
  name: "rudder-runtime-cache",
6698
6755
  version: "0.0.0",
@@ -12384,41 +12441,41 @@ var RudderApiClient = class {
12384
12441
  this.signal = opts.signal;
12385
12442
  this.recoverAuth = opts.recoverAuth;
12386
12443
  }
12387
- get(path24, opts) {
12388
- return this.request(path24, { method: "GET" }, opts);
12444
+ get(path25, opts) {
12445
+ return this.request(path25, { method: "GET" }, opts);
12389
12446
  }
12390
- post(path24, body, opts) {
12391
- return this.request(path24, {
12447
+ post(path25, body, opts) {
12448
+ return this.request(path25, {
12392
12449
  method: "POST",
12393
12450
  body: body === void 0 ? void 0 : JSON.stringify(body)
12394
12451
  }, opts);
12395
12452
  }
12396
- postForm(path24, form, opts) {
12397
- return this.request(path24, {
12453
+ postForm(path25, form, opts) {
12454
+ return this.request(path25, {
12398
12455
  method: "POST",
12399
12456
  body: form
12400
12457
  }, opts);
12401
12458
  }
12402
- patch(path24, body, opts) {
12403
- return this.request(path24, {
12459
+ patch(path25, body, opts) {
12460
+ return this.request(path25, {
12404
12461
  method: "PATCH",
12405
12462
  body: body === void 0 ? void 0 : JSON.stringify(body)
12406
12463
  }, opts);
12407
12464
  }
12408
- put(path24, body, opts) {
12409
- return this.request(path24, {
12465
+ put(path25, body, opts) {
12466
+ return this.request(path25, {
12410
12467
  method: "PUT",
12411
12468
  body: body === void 0 ? void 0 : JSON.stringify(body)
12412
12469
  }, opts);
12413
12470
  }
12414
- delete(path24, opts) {
12415
- return this.request(path24, { method: "DELETE" }, opts);
12471
+ delete(path25, opts) {
12472
+ return this.request(path25, { method: "DELETE" }, opts);
12416
12473
  }
12417
12474
  setApiKey(apiKey) {
12418
12475
  this.apiKey = apiKey?.trim() || void 0;
12419
12476
  }
12420
- async request(path24, init, opts, hasRetriedAuth = false) {
12421
- const url = buildUrl(this.apiBase, path24);
12477
+ async request(path25, init, opts, hasRetriedAuth = false) {
12478
+ const url = buildUrl(this.apiBase, path25);
12422
12479
  const headers = {
12423
12480
  accept: "application/json",
12424
12481
  ...toStringRecord(init.headers)
@@ -12449,13 +12506,13 @@ var RudderApiClient = class {
12449
12506
  const apiError = await toApiError(response);
12450
12507
  if (!hasRetriedAuth && this.recoverAuth) {
12451
12508
  const recoveredToken = await this.recoverAuth({
12452
- path: path24,
12509
+ path: path25,
12453
12510
  method: String(init.method ?? "GET").toUpperCase(),
12454
12511
  error: apiError
12455
12512
  });
12456
12513
  if (recoveredToken) {
12457
12514
  this.setApiKey(recoveredToken);
12458
- return this.request(path24, init, opts, true);
12515
+ return this.request(path25, init, opts, true);
12459
12516
  }
12460
12517
  }
12461
12518
  throw apiError;
@@ -12474,8 +12531,8 @@ function shouldAttachAgentContext(method) {
12474
12531
  const normalized = String(method ?? "GET").toUpperCase();
12475
12532
  return normalized !== "GET" && normalized !== "HEAD";
12476
12533
  }
12477
- function buildUrl(apiBase, path24) {
12478
- const normalizedPath = path24.startsWith("/") ? path24 : `/${path24}`;
12534
+ function buildUrl(apiBase, path25) {
12535
+ const normalizedPath = path25.startsWith("/") ? path25 : `/${path25}`;
12479
12536
  const [pathname, query] = normalizedPath.split("?");
12480
12537
  const url = new URL2(apiBase);
12481
12538
  url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
@@ -14934,8 +14991,8 @@ function registerActivityCommands(program) {
14934
14991
  if (opts.entityType) params.set("entityType", opts.entityType);
14935
14992
  if (opts.entityId) params.set("entityId", opts.entityId);
14936
14993
  const query = params.toString();
14937
- const path24 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
14938
- const rows = await ctx.api.get(path24) ?? [];
14994
+ const path25 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
14995
+ const rows = await ctx.api.get(path25) ?? [];
14939
14996
  if (ctx.json) {
14940
14997
  printOutput(rows, { json: true });
14941
14998
  return;
@@ -19868,8 +19925,8 @@ function registerUserCommands(program) {
19868
19925
  appendParam(params, "limit", opts.limit);
19869
19926
  appendParam(params, "cursor", opts.cursor);
19870
19927
  const query = params.toString();
19871
- const path24 = `/api/orgs/${ctx.orgId}/users/${encodeURIComponent(userId)}/activity-ledger${query ? `?${query}` : ""}`;
19872
- const result = await ctx.api.get(path24);
19928
+ const path25 = `/api/orgs/${ctx.orgId}/users/${encodeURIComponent(userId)}/activity-ledger${query ? `?${query}` : ""}`;
19929
+ const result = await ctx.api.get(path25);
19873
19930
  if (ctx.json) {
19874
19931
  printOutput(result, { json: true });
19875
19932
  return;
@@ -20681,22 +20738,48 @@ init_onboard();
20681
20738
  init_run();
20682
20739
 
20683
20740
  // src/commands/start.ts
20684
- init_home();
20685
- init_install2();
20686
- init_install();
20687
20741
  import * as p15 from "@clack/prompts";
20688
20742
  import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
20689
- import { createHash as createHash2, randomUUID } from "node:crypto";
20690
- import { createWriteStream, constants as fsConstants, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
20691
- import { access, chmod, copyFile as copyFile2, cp as cp2, lstat, mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile6, rm as rm2, stat as stat5, utimes, writeFile as writeFile3 } from "node:fs/promises";
20743
+ import { createHash as createHash4, randomUUID } from "node:crypto";
20744
+ import { constants as fsConstants, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "node:fs";
20745
+ import { access, chmod, copyFile as copyFile2, cp as cp2, lstat, mkdir as mkdir4, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile6, rm as rm3, stat as stat5, utimes, writeFile as writeFile3 } from "node:fs/promises";
20692
20746
  import { homedir, tmpdir } from "node:os";
20693
- import path22 from "node:path";
20694
- import { Readable, Transform } from "node:stream";
20695
- import { pipeline } from "node:stream/promises";
20696
- import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers";
20747
+ import path23 from "node:path";
20748
+ import { clearTimeout as clearTimeout3, setTimeout as setTimeout3 } from "node:timers";
20697
20749
  import { setTimeout as delay2 } from "node:timers/promises";
20698
20750
  import pc14 from "picocolors";
20699
20751
 
20752
+ // src/checksum-manifest.ts
20753
+ function parseChecksumFile(contents) {
20754
+ const checksums = /* @__PURE__ */ new Map();
20755
+ for (const [index, line] of contents.split(/\r?\n/).entries()) {
20756
+ if (!line.trim()) continue;
20757
+ const match = line.match(/^([a-fA-F0-9]{64})[ \t]+\*?(\S+)[ \t]*$/);
20758
+ if (!match) {
20759
+ throw new Error(`Invalid SHA-256 checksum manifest line ${index + 1}.`);
20760
+ }
20761
+ const assetName = match[2];
20762
+ if (checksums.has(assetName)) {
20763
+ throw new Error(`Duplicate SHA-256 checksum manifest entry for ${assetName}.`);
20764
+ }
20765
+ checksums.set(assetName, match[1].toLowerCase());
20766
+ }
20767
+ if (checksums.size === 0) throw new Error("Desktop release checksum manifest is empty.");
20768
+ return checksums;
20769
+ }
20770
+
20771
+ // src/commands/start.ts
20772
+ init_home();
20773
+
20774
+ // src/desktop-download.ts
20775
+ import { createHash as createHash3 } from "node:crypto";
20776
+ import { createWriteStream as createWriteStream2, mkdirSync } from "node:fs";
20777
+ import { rm as rm2 } from "node:fs/promises";
20778
+ import path22 from "node:path";
20779
+ import { Readable as Readable2, Transform as Transform2 } from "node:stream";
20780
+ import { pipeline as pipeline2 } from "node:stream/promises";
20781
+ import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers";
20782
+
20700
20783
  // src/utils/progress.ts
20701
20784
  var BYTE_UNITS = ["B", "KB", "MB", "GB", "TB"];
20702
20785
  function formatBytes(bytes) {
@@ -20804,9 +20887,201 @@ function createByteProgress(label, options = {}) {
20804
20887
  };
20805
20888
  }
20806
20889
 
20890
+ // src/desktop-download.ts
20891
+ var DEFAULT_DESKTOP_RELEASE_REPO = "Undertone0809/rudder";
20892
+ var DEFAULT_DESKTOP_RELEASE_MIRROR_BASE_URL = "https://rudder-releases-cn-1302936001.cos.ap-shanghai.myqcloud.com";
20893
+ var GITHUB_ASSET_DOWNLOAD_ACCEPT = "application/octet-stream";
20894
+ var DESKTOP_DOWNLOAD_SOURCE_PROBE_TIMEOUT_MS = 3e3;
20895
+ var DESKTOP_ASSET_RESPONSE_TIMEOUT_MS = 3e4;
20896
+ var DESKTOP_ASSET_IDLE_TIMEOUT_MS = 3e4;
20897
+ function resolveDesktopDownloadSource(optionValue, env = process.env) {
20898
+ const value = optionValue?.trim() || env.RUDDER_DOWNLOAD_SOURCE?.trim() || "auto";
20899
+ if (value === "auto" || value === "cn" || value === "global") return value;
20900
+ throw new Error(`Desktop download source must be auto, cn, or global. Received ${value}.`);
20901
+ }
20902
+ function normalizeReleaseMirrorBaseUrl(value) {
20903
+ let parsed;
20904
+ try {
20905
+ parsed = new URL(value);
20906
+ } catch {
20907
+ throw new Error(`Invalid Rudder release mirror base URL: ${value}`);
20908
+ }
20909
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
20910
+ throw new Error(`Rudder release mirror base URL must use HTTP or HTTPS: ${value}`);
20911
+ }
20912
+ parsed.pathname = parsed.pathname.replace(/\/+$/, "");
20913
+ parsed.search = "";
20914
+ parsed.hash = "";
20915
+ return parsed.toString().replace(/\/$/, "");
20916
+ }
20917
+ function resolveDesktopReleaseMirrorBaseUrl(repo, env = process.env) {
20918
+ const override = env.RUDDER_RELEASE_MIRROR_BASE_URL?.trim();
20919
+ if (override) return normalizeReleaseMirrorBaseUrl(override);
20920
+ return repo === DEFAULT_DESKTOP_RELEASE_REPO ? DEFAULT_DESKTOP_RELEASE_MIRROR_BASE_URL : null;
20921
+ }
20922
+ function encodeReleaseTagForDownloadUrl(tag) {
20923
+ return tag.split("/").map((segment) => encodeURIComponent(segment)).join("/");
20924
+ }
20925
+ function buildReleaseMirrorAssetDownloadUrl(baseUrl, tag, assetName) {
20926
+ const encodedTag = encodeReleaseTagForDownloadUrl(tag);
20927
+ return `${normalizeReleaseMirrorBaseUrl(baseUrl)}/releases/${encodedTag}/${encodeURIComponent(assetName)}`;
20928
+ }
20929
+ function githubAssetDownloadUrls(asset) {
20930
+ return [asset.browser_download_url, asset.url].filter((url) => Boolean(url));
20931
+ }
20932
+ function uniqueAssetDownloadUrls(asset) {
20933
+ const urls = asset.download_urls ?? githubAssetDownloadUrls(asset);
20934
+ return Array.from(new Set(urls));
20935
+ }
20936
+ function withDesktopDownloadOrigins(asset, origins, options) {
20937
+ const githubUrls = githubAssetDownloadUrls(asset);
20938
+ const urls = origins.flatMap((origin) => {
20939
+ if (origin === "github") return githubUrls;
20940
+ if (!options.mirrorBaseUrl) return [];
20941
+ return [buildReleaseMirrorAssetDownloadUrl(options.mirrorBaseUrl, options.tag, asset.name)];
20942
+ });
20943
+ return { ...asset, download_urls: Array.from(new Set(urls)) };
20944
+ }
20945
+ function downloadHeadersForAssetUrl(asset, url) {
20946
+ return {
20947
+ Accept: url === asset.url ? GITHUB_ASSET_DOWNLOAD_ACCEPT : "*/*",
20948
+ "User-Agent": "rudder-cli-installer"
20949
+ };
20950
+ }
20951
+ function formatFetchError(error) {
20952
+ if (!(error instanceof Error)) return String(error);
20953
+ const cause = error.cause;
20954
+ if (cause instanceof Error) {
20955
+ const code = cause.code;
20956
+ const suffix = typeof code === "string" ? ` [${code}]` : "";
20957
+ return `${error.message}: ${cause.message}${suffix}`;
20958
+ }
20959
+ return error.message;
20960
+ }
20961
+ function contentLengthFromHeaders(headers) {
20962
+ const raw = headers.get("content-length");
20963
+ if (!raw) return null;
20964
+ const value = Number(raw);
20965
+ return Number.isFinite(value) && value > 0 ? value : null;
20966
+ }
20967
+ async function fetchWithTimeout(url, init, timeoutMs) {
20968
+ const controller = new AbortController();
20969
+ const timeout = setTimeout2(() => controller.abort(), timeoutMs);
20970
+ try {
20971
+ return await fetch(url, { ...init, signal: controller.signal });
20972
+ } finally {
20973
+ clearTimeout2(timeout);
20974
+ }
20975
+ }
20976
+ async function probeDesktopDownloadUrl(url, timeoutMs = DESKTOP_DOWNLOAD_SOURCE_PROBE_TIMEOUT_MS) {
20977
+ const startedAt = Date.now();
20978
+ try {
20979
+ const response = await fetchWithTimeout(
20980
+ url,
20981
+ {
20982
+ headers: {
20983
+ Accept: "*/*",
20984
+ Range: "bytes=0-0",
20985
+ "User-Agent": "rudder-cli-installer"
20986
+ }
20987
+ },
20988
+ timeoutMs
20989
+ );
20990
+ if (!response.ok) return null;
20991
+ await response.body?.cancel();
20992
+ return Date.now() - startedAt;
20993
+ } catch {
20994
+ return null;
20995
+ }
20996
+ }
20997
+ async function resolveDesktopDownloadOrigins(options) {
20998
+ if (!options.mirrorBaseUrl) return ["github"];
20999
+ if (options.source === "cn") return ["mirror", "github"];
21000
+ if (options.source === "global") return ["github", "mirror"];
21001
+ const mirrorUrl = buildReleaseMirrorAssetDownloadUrl(
21002
+ options.mirrorBaseUrl,
21003
+ options.tag,
21004
+ options.checksumAsset.name
21005
+ );
21006
+ const githubUrl = options.checksumAsset.browser_download_url;
21007
+ const [mirrorElapsed, githubElapsed] = await Promise.all([
21008
+ probeDesktopDownloadUrl(mirrorUrl, options.probeTimeoutMs),
21009
+ probeDesktopDownloadUrl(githubUrl, options.probeTimeoutMs)
21010
+ ]);
21011
+ if (mirrorElapsed !== null && (githubElapsed === null || mirrorElapsed <= githubElapsed)) {
21012
+ return ["mirror", "github"];
21013
+ }
21014
+ return mirrorElapsed === null ? ["github"] : ["github", "mirror"];
21015
+ }
21016
+ async function downloadAsset(asset, outputDir, progressFactory = createByteProgress, expectedChecksum, timeouts = {}) {
21017
+ mkdirSync(outputDir, { recursive: true });
21018
+ const outputPath = path22.join(outputDir, path22.basename(asset.name));
21019
+ const idleTimeoutMs = timeouts.idleMs ?? DESKTOP_ASSET_IDLE_TIMEOUT_MS;
21020
+ const responseTimeoutMs = timeouts.responseMs ?? DESKTOP_ASSET_RESPONSE_TIMEOUT_MS;
21021
+ const failures = [];
21022
+ for (const url of uniqueAssetDownloadUrls(asset)) {
21023
+ let progress = null;
21024
+ let idleTimeout = null;
21025
+ try {
21026
+ const response = await fetchWithTimeout(
21027
+ url,
21028
+ { headers: downloadHeadersForAssetUrl(asset, url) },
21029
+ responseTimeoutMs
21030
+ );
21031
+ if (!response.ok || !response.body) {
21032
+ failures.push(`Failed to download ${asset.name} from ${url} (${response.status}).`);
21033
+ continue;
21034
+ }
21035
+ const totalBytes = contentLengthFromHeaders(response.headers);
21036
+ progress = progressFactory(`Downloading ${asset.name}`);
21037
+ let receivedBytes = 0;
21038
+ const hash = createHash3("sha256");
21039
+ let monitor;
21040
+ const armIdleTimeout = () => {
21041
+ if (idleTimeout) clearTimeout2(idleTimeout);
21042
+ idleTimeout = setTimeout2(() => {
21043
+ monitor.destroy(new Error(`Download from ${url} made no progress for ${idleTimeoutMs}ms.`));
21044
+ }, idleTimeoutMs);
21045
+ };
21046
+ monitor = new Transform2({
21047
+ transform(chunk, encoding, callback) {
21048
+ const bytes = typeof chunk === "string" ? Buffer.from(chunk, encoding) : chunk;
21049
+ receivedBytes += bytes.length;
21050
+ hash.update(bytes);
21051
+ progress?.update(receivedBytes, totalBytes);
21052
+ armIdleTimeout();
21053
+ callback(null, chunk);
21054
+ }
21055
+ });
21056
+ progress.start(totalBytes);
21057
+ armIdleTimeout();
21058
+ await pipeline2(Readable2.fromWeb(response.body), monitor, createWriteStream2(outputPath));
21059
+ if (idleTimeout) clearTimeout2(idleTimeout);
21060
+ idleTimeout = null;
21061
+ const actualChecksum = hash.digest("hex");
21062
+ if (expectedChecksum && actualChecksum !== expectedChecksum.toLowerCase()) {
21063
+ progress.fail();
21064
+ progress = null;
21065
+ await rm2(outputPath, { force: true });
21066
+ failures.push(`Checksum mismatch for ${asset.name} from ${url}.`);
21067
+ continue;
21068
+ }
21069
+ progress.finish(receivedBytes, totalBytes);
21070
+ return outputPath;
21071
+ } catch (error) {
21072
+ if (idleTimeout) clearTimeout2(idleTimeout);
21073
+ progress?.fail();
21074
+ await rm2(outputPath, { force: true });
21075
+ failures.push(`Failed to download ${asset.name} from ${url}: ${formatFetchError(error)}.`);
21076
+ }
21077
+ }
21078
+ throw new Error(failures.join("\n"));
21079
+ }
21080
+
20807
21081
  // src/commands/start.ts
21082
+ init_install2();
21083
+ init_install();
20808
21084
  init_version();
20809
- var DEFAULT_DESKTOP_RELEASE_REPO = "Undertone0809/rudder";
20810
21085
  var DESKTOP_UPDATE_QUIT_ARG = "--rudder-update-quit";
20811
21086
  var DESKTOP_UPDATE_FORCE_ARG = "--rudder-update-force";
20812
21087
  var STABLE_SEMVER_RE = /^[0-9]+\.[0-9]+\.[0-9]+$/;
@@ -20818,7 +21093,6 @@ var DESKTOP_APP_NAME = "Rudder";
20818
21093
  var DESKTOP_METADATA_FILE = ".rudder-desktop-install.json";
20819
21094
  var DESKTOP_CHECKSUM_ASSET_NAME = "SHASUMS256.txt";
20820
21095
  var DESKTOP_ASSET_CACHE_DIR = "desktop-assets";
20821
- var GITHUB_ASSET_DOWNLOAD_ACCEPT = "application/octet-stream";
20822
21096
  var DEFAULT_DESKTOP_ASSET_CACHE_MAX_ENTRIES = 2;
20823
21097
  var DEFAULT_DESKTOP_ASSET_CACHE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
20824
21098
  var DEFAULT_DESKTOP_ASSET_CACHE_MAX_BYTES = 768 * 1024 * 1024;
@@ -20961,10 +21235,10 @@ function createDesktopApplySignalController() {
20961
21235
  if (settled) return;
20962
21236
  settled = true;
20963
21237
  forceWaiters.delete(finish);
20964
- clearTimeout2(timer);
21238
+ clearTimeout3(timer);
20965
21239
  resolve(force);
20966
21240
  };
20967
- const timer = setTimeout2(() => finish(false), timeoutMs);
21241
+ const timer = setTimeout3(() => finish(false), timeoutMs);
20968
21242
  timer.unref?.();
20969
21243
  forceWaiters.add(finish);
20970
21244
  });
@@ -20995,7 +21269,7 @@ function compareStableSemver(a, b) {
20995
21269
  }
20996
21270
  async function fetchLatestCliVersion() {
20997
21271
  const controller = new AbortController();
20998
- const timeout = setTimeout2(() => controller.abort(), 2e3);
21272
+ const timeout = setTimeout3(() => controller.abort(), 2e3);
20999
21273
  try {
21000
21274
  const response = await fetch(CLI_REGISTRY_LATEST_URL, {
21001
21275
  signal: controller.signal,
@@ -21007,7 +21281,7 @@ async function fetchLatestCliVersion() {
21007
21281
  } catch {
21008
21282
  return null;
21009
21283
  } finally {
21010
- clearTimeout2(timeout);
21284
+ clearTimeout3(timeout);
21011
21285
  }
21012
21286
  }
21013
21287
  async function getCliUpdateNotice(currentVersion) {
@@ -21048,38 +21322,38 @@ function resolveDesktopAssetTarget(platform = process.platform, arch = process.a
21048
21322
  throw new Error(`Rudder Desktop does not publish portable assets for ${platform}.`);
21049
21323
  }
21050
21324
  function resolveDefaultDesktopInstallRoot(target, env = process.env, homeDir = homedir()) {
21051
- if (target.platform === "macos") return path22.join(homeDir, "Applications");
21325
+ if (target.platform === "macos") return path23.join(homeDir, "Applications");
21052
21326
  if (target.platform === "windows") {
21053
- const localAppData = env.LOCALAPPDATA?.trim() || path22.join(homeDir, "AppData", "Local");
21054
- return path22.join(localAppData, "Programs", DESKTOP_APP_NAME);
21327
+ const localAppData = env.LOCALAPPDATA?.trim() || path23.join(homeDir, "AppData", "Local");
21328
+ return path23.join(localAppData, "Programs", DESKTOP_APP_NAME);
21055
21329
  }
21056
- return path22.join(homeDir, ".local", "share", "rudder");
21330
+ return path23.join(homeDir, ".local", "share", "rudder");
21057
21331
  }
21058
21332
  function resolveDesktopInstallPaths(target, installRoot) {
21059
- const root = path22.resolve(installRoot);
21333
+ const root = path23.resolve(installRoot);
21060
21334
  if (target.platform === "macos") {
21061
- const appPath2 = path22.join(root, `${DESKTOP_APP_NAME}.app`);
21335
+ const appPath2 = path23.join(root, `${DESKTOP_APP_NAME}.app`);
21062
21336
  return {
21063
21337
  installRoot: root,
21064
21338
  appPath: appPath2,
21065
- executablePath: path22.join(appPath2, "Contents", "MacOS", DESKTOP_APP_NAME),
21066
- metadataPath: path22.join(root, DESKTOP_METADATA_FILE)
21339
+ executablePath: path23.join(appPath2, "Contents", "MacOS", DESKTOP_APP_NAME),
21340
+ metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
21067
21341
  };
21068
21342
  }
21069
21343
  if (target.platform === "windows") {
21070
21344
  return {
21071
21345
  installRoot: root,
21072
21346
  appPath: root,
21073
- executablePath: path22.join(root, `${DESKTOP_APP_NAME}.exe`),
21074
- metadataPath: path22.join(root, DESKTOP_METADATA_FILE)
21347
+ executablePath: path23.join(root, `${DESKTOP_APP_NAME}.exe`),
21348
+ metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
21075
21349
  };
21076
21350
  }
21077
- const appPath = path22.join(root, `${DESKTOP_APP_NAME}.AppImage`);
21351
+ const appPath = path23.join(root, `${DESKTOP_APP_NAME}.AppImage`);
21078
21352
  return {
21079
21353
  installRoot: root,
21080
21354
  appPath,
21081
21355
  executablePath: appPath,
21082
- metadataPath: path22.join(root, DESKTOP_METADATA_FILE)
21356
+ metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
21083
21357
  };
21084
21358
  }
21085
21359
  function normalizeAssetName(name) {
@@ -21169,13 +21443,13 @@ function selectChecksummedDesktopAssetCandidate(candidates, checksums) {
21169
21443
  function selectChecksumAsset(assets) {
21170
21444
  return assets.find((asset) => asset.name.toLowerCase() === DESKTOP_CHECKSUM_ASSET_NAME.toLowerCase()) ?? null;
21171
21445
  }
21172
- async function fetchWithTimeout(url, init, timeoutMs) {
21446
+ async function fetchWithTimeout2(url, init, timeoutMs) {
21173
21447
  const controller = new AbortController();
21174
- const timeout = setTimeout2(() => controller.abort(), timeoutMs);
21448
+ const timeout = setTimeout3(() => controller.abort(), timeoutMs);
21175
21449
  try {
21176
21450
  return await fetch(url, { ...init, signal: controller.signal });
21177
21451
  } finally {
21178
- clearTimeout2(timeout);
21452
+ clearTimeout3(timeout);
21179
21453
  }
21180
21454
  }
21181
21455
  function githubApiHeaders() {
@@ -21187,7 +21461,7 @@ function githubApiHeaders() {
21187
21461
  var GITHUB_API_TIMEOUT_MS = 15e3;
21188
21462
  async function fetchGithubRelease(repo, tag) {
21189
21463
  const endpoint = tag === "latest" ? `https://api.github.com/repos/${repo}/releases/latest` : `https://api.github.com/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`;
21190
- const response = await fetchWithTimeout(endpoint, { headers: githubApiHeaders() }, GITHUB_API_TIMEOUT_MS);
21464
+ const response = await fetchWithTimeout2(endpoint, { headers: githubApiHeaders() }, GITHUB_API_TIMEOUT_MS);
21191
21465
  if (!response.ok) {
21192
21466
  throw new Error(`GitHub Release ${tag} was not found in ${repo} (${response.status}).`);
21193
21467
  }
@@ -21211,11 +21485,11 @@ function resolveDesktopShellAssetName(version, target) {
21211
21485
  if (target.platform === "windows") return `${DESKTOP_APP_NAME}-${version}-windows-x64-shell.zip`;
21212
21486
  return null;
21213
21487
  }
21214
- function encodeReleaseTagForDownloadUrl(tag) {
21488
+ function encodeReleaseTagForDownloadUrl2(tag) {
21215
21489
  return tag.split("/").map((segment) => encodeURIComponent(segment)).join("/");
21216
21490
  }
21217
21491
  function buildGithubReleaseAssetDownloadUrl(repo, tag, assetName) {
21218
- const encodedTag = encodeReleaseTagForDownloadUrl(tag);
21492
+ const encodedTag = encodeReleaseTagForDownloadUrl2(tag);
21219
21493
  return `https://github.com/${repo}/releases/download/${encodedTag}/${encodeURIComponent(assetName)}`;
21220
21494
  }
21221
21495
  function buildGithubReleaseAsset(repo, tag, assetName) {
@@ -21224,102 +21498,22 @@ function buildGithubReleaseAsset(repo, tag, assetName) {
21224
21498
  browser_download_url: buildGithubReleaseAssetDownloadUrl(repo, tag, assetName)
21225
21499
  };
21226
21500
  }
21227
- function uniqueAssetDownloadUrls(asset) {
21228
- const urls = [asset.browser_download_url, asset.url].filter((url) => Boolean(url));
21229
- return Array.from(new Set(urls));
21230
- }
21231
- function downloadHeadersForAssetUrl(asset, url) {
21232
- return {
21233
- Accept: url === asset.url ? GITHUB_ASSET_DOWNLOAD_ACCEPT : "*/*",
21234
- "User-Agent": "rudder-cli-installer"
21235
- };
21236
- }
21237
- function formatFetchError(error) {
21238
- if (!(error instanceof Error)) return String(error);
21239
- const cause = error.cause;
21240
- if (cause instanceof Error) {
21241
- const code = cause.code;
21242
- const suffix = typeof code === "string" ? ` [${code}]` : "";
21243
- return `${error.message}: ${cause.message}${suffix}`;
21244
- }
21245
- return error.message;
21246
- }
21247
- function contentLengthFromHeaders(headers) {
21248
- const raw = headers.get("content-length");
21249
- if (!raw) return null;
21250
- const value = Number(raw);
21251
- return Number.isFinite(value) && value > 0 ? value : null;
21252
- }
21253
- async function downloadAsset(asset, outputDir, progressFactory = createByteProgress) {
21254
- mkdirSync(outputDir, { recursive: true });
21255
- const outputPath = path22.join(outputDir, path22.basename(asset.name));
21256
- const ASSET_DOWNLOAD_TIMEOUT_MS = 6e5;
21257
- let response = null;
21258
- const failures = [];
21259
- for (const url of uniqueAssetDownloadUrls(asset)) {
21260
- try {
21261
- const candidate = await fetchWithTimeout(
21262
- url,
21263
- { headers: downloadHeadersForAssetUrl(asset, url) },
21264
- ASSET_DOWNLOAD_TIMEOUT_MS
21265
- );
21266
- if (candidate.ok && candidate.body) {
21267
- response = candidate;
21268
- break;
21269
- }
21270
- failures.push(`Failed to download ${asset.name} from ${url} (${candidate.status}).`);
21271
- } catch (error) {
21272
- failures.push(`Failed to download ${asset.name} from ${url}: ${formatFetchError(error)}.`);
21273
- }
21274
- }
21275
- if (!response) {
21276
- throw new Error(failures.join("\n"));
21277
- }
21278
- const totalBytes = contentLengthFromHeaders(response.headers);
21279
- const progress = progressFactory(`Downloading ${asset.name}`);
21280
- let receivedBytes = 0;
21281
- const monitor = new Transform({
21282
- transform(chunk, _encoding, callback) {
21283
- receivedBytes += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
21284
- progress.update(receivedBytes, totalBytes);
21285
- callback(null, chunk);
21286
- }
21287
- });
21288
- progress.start(totalBytes);
21289
- try {
21290
- await pipeline(Readable.fromWeb(response.body), monitor, createWriteStream(outputPath));
21291
- progress.finish(receivedBytes, totalBytes);
21292
- } catch (error) {
21293
- progress.fail();
21294
- throw error;
21295
- }
21296
- return outputPath;
21297
- }
21298
21501
  function checksumForFile(filePath) {
21299
- const hash = createHash2("sha256");
21502
+ const hash = createHash4("sha256");
21300
21503
  hash.update(readFileSync2(filePath));
21301
21504
  return hash.digest("hex");
21302
21505
  }
21303
- function parseChecksumFile(contents) {
21304
- const checksums = /* @__PURE__ */ new Map();
21305
- for (const line of contents.split(/\r?\n/)) {
21306
- const match = line.match(/^([a-fA-F0-9]{64})\s+\*?(.+)$/);
21307
- if (!match) continue;
21308
- checksums.set(match[2].trim(), match[1].toLowerCase());
21309
- }
21310
- return checksums;
21311
- }
21312
21506
  function resolveAssetChecksum(checksums, assetName) {
21313
- const expected = checksums.get(path22.basename(assetName));
21507
+ const expected = checksums.get(path23.basename(assetName));
21314
21508
  if (!expected) {
21315
- throw new Error(`Desktop release checksums do not include ${path22.basename(assetName)}.`);
21509
+ throw new Error(`Desktop release checksums do not include ${path23.basename(assetName)}.`);
21316
21510
  }
21317
21511
  return expected;
21318
21512
  }
21319
21513
  function assertChecksumMatch(filePath, expected) {
21320
21514
  const actual = checksumForFile(filePath);
21321
21515
  if (actual !== expected.toLowerCase()) {
21322
- throw new Error(`Checksum mismatch for ${path22.basename(filePath)}.`);
21516
+ throw new Error(`Checksum mismatch for ${path23.basename(filePath)}.`);
21323
21517
  }
21324
21518
  return actual;
21325
21519
  }
@@ -21338,10 +21532,10 @@ function normalizeDesktopAssetChecksum(checksum) {
21338
21532
  return normalized;
21339
21533
  }
21340
21534
  function resolveDesktopAssetCacheDir(assetChecksum, homeDir = resolveRudderHomeDir()) {
21341
- return path22.join(homeDir, DESKTOP_ASSET_CACHE_DIR, normalizeDesktopAssetChecksum(assetChecksum));
21535
+ return path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR, normalizeDesktopAssetChecksum(assetChecksum));
21342
21536
  }
21343
21537
  function resolveDesktopCachedAssetPath(assetName, assetChecksum, homeDir = resolveRudderHomeDir()) {
21344
- return path22.join(resolveDesktopAssetCacheDir(assetChecksum, homeDir), path22.basename(assetName));
21538
+ return path23.join(resolveDesktopAssetCacheDir(assetChecksum, homeDir), path23.basename(assetName));
21345
21539
  }
21346
21540
  async function pruneDesktopAssetCache(options = {}) {
21347
21541
  const homeDir = options.homeDir ?? resolveRudderHomeDir();
@@ -21362,7 +21556,7 @@ async function pruneDesktopAssetCache(options = {}) {
21362
21556
  const warnings = [];
21363
21557
  for (const entry of deletions) {
21364
21558
  try {
21365
- await rm2(entry.cacheDir, { recursive: true, force: true });
21559
+ await rm3(entry.cacheDir, { recursive: true, force: true });
21366
21560
  deleted.push({
21367
21561
  cacheDir: entry.cacheDir,
21368
21562
  checksum: entry.checksum,
@@ -21387,7 +21581,7 @@ async function maybePruneDesktopAssetCache(options) {
21387
21581
  return result.deleted.length > 0 || result.warnings.length > 0 ? result : null;
21388
21582
  }
21389
21583
  async function scanDesktopAssetCacheEntries(homeDir) {
21390
- const cacheRoot = path22.join(homeDir, DESKTOP_ASSET_CACHE_DIR);
21584
+ const cacheRoot = path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR);
21391
21585
  const dirents = await readdir3(cacheRoot, { withFileTypes: true }).catch(() => null);
21392
21586
  if (!dirents) return [];
21393
21587
  const entries = [];
@@ -21399,7 +21593,7 @@ async function scanDesktopAssetCacheEntries(homeDir) {
21399
21593
  } catch {
21400
21594
  continue;
21401
21595
  }
21402
- const cacheDir = path22.join(cacheRoot, dirent.name);
21596
+ const cacheDir = path23.join(cacheRoot, dirent.name);
21403
21597
  const stats = await desktopCacheDirectoryStats(cacheDir);
21404
21598
  entries.push({
21405
21599
  cacheDir,
@@ -21423,7 +21617,7 @@ async function desktopCacheDirectoryStats(targetPath) {
21423
21617
  let lastUsedAtMs = Number(fallbackStat?.mtimeMs ?? 0);
21424
21618
  for (const dirent of dirents) {
21425
21619
  if (dirent.isSymbolicLink()) continue;
21426
- const entryPath = path22.join(targetPath, dirent.name);
21620
+ const entryPath = path23.join(targetPath, dirent.name);
21427
21621
  const entryStat = await stat5(entryPath).catch(() => null);
21428
21622
  if (!entryStat) continue;
21429
21623
  lastUsedAtMs = Math.max(lastUsedAtMs, Number(entryStat.mtimeMs ?? 0));
@@ -21497,21 +21691,21 @@ async function downloadDesktopAssetWithCache(asset, expectedChecksum, options =
21497
21691
  await touchDesktopCachedAsset(cachePath);
21498
21692
  return { path: cachePath, checksum, cacheStatus: "hit" };
21499
21693
  } catch {
21500
- await rm2(cachePath, { force: true });
21694
+ await rm3(cachePath, { force: true });
21501
21695
  }
21502
21696
  }
21503
- const outputDir = options.outputDir ?? await mkdtemp2(path22.join(tmpdir(), "rudder-desktop-installer."));
21697
+ const outputDir = options.outputDir ?? await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
21504
21698
  const removeOutputDir = options.outputDir ? false : true;
21505
21699
  try {
21506
- const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory);
21700
+ const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory, normalizedChecksum);
21507
21701
  const checksum = assertChecksumMatch(downloadedPath, normalizedChecksum);
21508
- await mkdir4(path22.dirname(cachePath), { recursive: true });
21509
- if (path22.resolve(downloadedPath) !== path22.resolve(cachePath)) {
21702
+ await mkdir4(path23.dirname(cachePath), { recursive: true });
21703
+ if (path23.resolve(downloadedPath) !== path23.resolve(cachePath)) {
21510
21704
  await copyFile2(downloadedPath, cachePath);
21511
21705
  }
21512
21706
  return { path: cachePath, checksum, cacheStatus: "miss" };
21513
21707
  } finally {
21514
- if (removeOutputDir) await rm2(outputDir, { recursive: true, force: true });
21708
+ if (removeOutputDir) await rm3(outputDir, { recursive: true, force: true });
21515
21709
  }
21516
21710
  }
21517
21711
  async function pathExists2(targetPath) {
@@ -21523,8 +21717,8 @@ async function pathExists2(targetPath) {
21523
21717
  }
21524
21718
  }
21525
21719
  function resolveDesktopInstallLockPath(paths) {
21526
- const installRootHash = createHash2("sha256").update(path22.resolve(paths.installRoot)).digest("hex").slice(0, 16);
21527
- return path22.join(path22.dirname(paths.appPath), `.rudder-desktop-install-${installRootHash}.lock`);
21720
+ const installRootHash = createHash4("sha256").update(path23.resolve(paths.installRoot)).digest("hex").slice(0, 16);
21721
+ return path23.join(path23.dirname(paths.appPath), `.rudder-desktop-install-${installRootHash}.lock`);
21528
21722
  }
21529
21723
  async function readDesktopInstallLock(lockPath) {
21530
21724
  try {
@@ -21544,14 +21738,14 @@ async function readDesktopInstallLock(lockPath) {
21544
21738
  }
21545
21739
  async function withDesktopInstallLock(paths, fn, options = {}) {
21546
21740
  const lockPath = resolveDesktopInstallLockPath(paths);
21547
- const lockDir = path22.dirname(lockPath);
21741
+ const lockDir = path23.dirname(lockPath);
21548
21742
  const timeoutMs = options.timeoutMs ?? DESKTOP_INSTALL_LOCK_TIMEOUT_MS;
21549
21743
  const pollMs = options.pollMs ?? DESKTOP_INSTALL_LOCK_POLL_MS;
21550
21744
  const startedAt = Date.now();
21551
21745
  const payload = {
21552
21746
  lockId: randomUUID(),
21553
21747
  pid: process.pid,
21554
- installRoot: path22.resolve(paths.installRoot),
21748
+ installRoot: path23.resolve(paths.installRoot),
21555
21749
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
21556
21750
  };
21557
21751
  await mkdir4(lockDir, { recursive: true });
@@ -21566,7 +21760,7 @@ async function withDesktopInstallLock(paths, fn, options = {}) {
21566
21760
  const existing = await readDesktopInstallLock(lockPath);
21567
21761
  const stale = !existing || !processExists(existing.pid);
21568
21762
  if (stale) {
21569
- await rm2(lockPath, { force: true });
21763
+ await rm3(lockPath, { force: true });
21570
21764
  continue;
21571
21765
  }
21572
21766
  if (Date.now() - startedAt >= timeoutMs) {
@@ -21582,7 +21776,7 @@ async function withDesktopInstallLock(paths, fn, options = {}) {
21582
21776
  } finally {
21583
21777
  const existing = await readDesktopInstallLock(lockPath);
21584
21778
  if (existing?.lockId === payload.lockId) {
21585
- await rm2(lockPath, { force: true });
21779
+ await rm3(lockPath, { force: true });
21586
21780
  }
21587
21781
  }
21588
21782
  }
@@ -21604,7 +21798,7 @@ function powershellQuote(value) {
21604
21798
  return `'${value.replaceAll("'", "''")}'`;
21605
21799
  }
21606
21800
  function buildWindowsZipExtractCommand(zipPath, outputDir) {
21607
- return { command: "powershell.exe", args: ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", `Expand-Archive -LiteralPath ${powershellQuote(zipPath)} -DestinationPath ${powershellQuote(outputDir)} -Force`] };
21801
+ return { command: "tar.exe", args: ["-xf", zipPath, "-C", outputDir] };
21608
21802
  }
21609
21803
  function buildWindowsRobocopyMirrorCommand(sourcePath, destinationPath) {
21610
21804
  return {
@@ -21616,7 +21810,7 @@ function isSuccessfulRobocopyExitCode(status) {
21616
21810
  return typeof status === "number" && status >= 0 && status <= 7;
21617
21811
  }
21618
21812
  async function extractZip(zipPath, outputDir, target) {
21619
- await rm2(outputDir, { recursive: true, force: true });
21813
+ await rm3(outputDir, { recursive: true, force: true });
21620
21814
  await mkdir4(outputDir, { recursive: true });
21621
21815
  if (target.platform === "macos") {
21622
21816
  runChecked("ditto", ["-x", "-k", zipPath, outputDir]);
@@ -21633,7 +21827,7 @@ async function findPath(root, predicate, maxDepth = 5) {
21633
21827
  async function visit(dir, depth) {
21634
21828
  const entries = await readdir3(dir, { withFileTypes: true });
21635
21829
  for (const entry of entries) {
21636
- const fullPath = path22.join(dir, entry.name);
21830
+ const fullPath = path23.join(dir, entry.name);
21637
21831
  if (predicate(fullPath, entry.isDirectory())) return fullPath;
21638
21832
  if (entry.isDirectory() && depth < maxDepth) {
21639
21833
  const nested = await visit(fullPath, depth + 1);
@@ -21645,18 +21839,18 @@ async function findPath(root, predicate, maxDepth = 5) {
21645
21839
  return await visit(root, 0);
21646
21840
  }
21647
21841
  async function findMacApp(extractDir) {
21648
- const direct = path22.join(extractDir, `${DESKTOP_APP_NAME}.app`);
21842
+ const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.app`);
21649
21843
  if (await pathExists2(direct)) return direct;
21650
- const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory && path22.basename(filePath) === `${DESKTOP_APP_NAME}.app`);
21844
+ const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory && path23.basename(filePath) === `${DESKTOP_APP_NAME}.app`);
21651
21845
  if (!found) throw new Error(`Portable macOS archive did not contain ${DESKTOP_APP_NAME}.app.`);
21652
21846
  return found;
21653
21847
  }
21654
21848
  async function findWindowsAppDir(extractDir) {
21655
- const direct = path22.join(extractDir, `${DESKTOP_APP_NAME}.exe`);
21849
+ const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.exe`);
21656
21850
  if (await pathExists2(direct)) return extractDir;
21657
- const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory && path22.basename(filePath).toLowerCase() === `${DESKTOP_APP_NAME.toLowerCase()}.exe`);
21851
+ const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory && path23.basename(filePath).toLowerCase() === `${DESKTOP_APP_NAME.toLowerCase()}.exe`);
21658
21852
  if (!executable) throw new Error(`Portable Windows archive did not contain ${DESKTOP_APP_NAME}.exe.`);
21659
- return path22.dirname(executable);
21853
+ return path23.dirname(executable);
21660
21854
  }
21661
21855
  async function readInstallMetadata(metadataPath) {
21662
21856
  try {
@@ -21724,7 +21918,7 @@ async function waitForUpdateQuitResponse(responsePath, timeoutMs = 8e3) {
21724
21918
  }
21725
21919
  async function requestDesktopQuit(executablePath, target, options = {}) {
21726
21920
  if (!await pathExists2(executablePath)) return { ok: true, status: "not_running" };
21727
- const responsePath = path22.join(tmpdir(), `rudder-update-quit-${process.pid}-${Date.now()}.json`);
21921
+ const responsePath = path23.join(tmpdir(), `rudder-update-quit-${process.pid}-${Date.now()}.json`);
21728
21922
  const result = spawnSync4(executablePath, [
21729
21923
  `${DESKTOP_UPDATE_QUIT_ARG}=${responsePath}`,
21730
21924
  ...options.forceUpdate ? [DESKTOP_UPDATE_FORCE_ARG] : []
@@ -21738,7 +21932,7 @@ async function requestDesktopQuit(executablePath, target, options = {}) {
21738
21932
  try {
21739
21933
  return await waitForUpdateQuitResponse(responsePath, options.responseTimeoutMs);
21740
21934
  } finally {
21741
- await rm2(responsePath, { force: true });
21935
+ await rm3(responsePath, { force: true });
21742
21936
  }
21743
21937
  }
21744
21938
  function processExists(pid) {
@@ -21774,7 +21968,7 @@ async function waitForProcessesExit(pids, waitForExit) {
21774
21968
  async function removePathWithRetry(targetPath, attempts = 5) {
21775
21969
  for (let attempt = 0; attempt < attempts; attempt += 1) {
21776
21970
  try {
21777
- await rm2(targetPath, { recursive: true, force: true });
21971
+ await rm3(targetPath, { recursive: true, force: true });
21778
21972
  if (!await pathExists2(targetPath)) return true;
21779
21973
  } catch {
21780
21974
  }
@@ -21888,7 +22082,7 @@ async function installPortableDesktop(installerPath, paths, target) {
21888
22082
  await chmod(paths.appPath, 493);
21889
22083
  return;
21890
22084
  }
21891
- const extractDir = await mkdtemp2(path22.join(tmpdir(), "rudder-desktop-extract."));
22085
+ const extractDir = await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-extract."));
21892
22086
  try {
21893
22087
  await extractZip(installerPath, extractDir, target);
21894
22088
  if (target.platform === "macos") {
@@ -21897,10 +22091,10 @@ async function installPortableDesktop(installerPath, paths, target) {
21897
22091
  return;
21898
22092
  }
21899
22093
  const appSource = await findWindowsAppDir(extractDir);
21900
- await mkdir4(path22.dirname(paths.installRoot), { recursive: true });
22094
+ await mkdir4(path23.dirname(paths.installRoot), { recursive: true });
21901
22095
  await copyPortableAppBundle(appSource, paths.installRoot);
21902
22096
  } finally {
21903
- await rm2(extractDir, { recursive: true, force: true });
22097
+ await rm3(extractDir, { recursive: true, force: true });
21904
22098
  }
21905
22099
  }
21906
22100
  async function copyPortableAppBundle(sourcePath, destinationPath) {
@@ -21938,12 +22132,12 @@ function buildLinuxDesktopEntry(executablePath) {
21938
22132
  ].join("\n");
21939
22133
  }
21940
22134
  async function writeLinuxLaunchers(paths) {
21941
- const desktopDir = path22.join(homedir(), ".local", "share", "applications");
22135
+ const desktopDir = path23.join(homedir(), ".local", "share", "applications");
21942
22136
  await mkdir4(desktopDir, { recursive: true });
21943
- await writeFile3(path22.join(desktopDir, "rudder.desktop"), buildLinuxDesktopEntry(paths.executablePath), "utf8");
21944
- const binDir = path22.join(homedir(), ".local", "bin");
22137
+ await writeFile3(path23.join(desktopDir, "rudder.desktop"), buildLinuxDesktopEntry(paths.executablePath), "utf8");
22138
+ const binDir = path23.join(homedir(), ".local", "bin");
21945
22139
  await mkdir4(binDir, { recursive: true });
21946
- const wrapperPath = path22.join(binDir, "rudder-desktop");
22140
+ const wrapperPath = path23.join(binDir, "rudder-desktop");
21947
22141
  const escaped = paths.executablePath.replaceAll("'", `'"'"'`);
21948
22142
  await writeFile3(wrapperPath, `#!/bin/sh
21949
22143
  exec '${escaped}' "$@"
@@ -21951,13 +22145,13 @@ exec '${escaped}' "$@"
21951
22145
  await chmod(wrapperPath, 493);
21952
22146
  }
21953
22147
  function buildWindowsShortcutScript(executablePath) {
21954
- const appData = process.env.APPDATA?.trim() || path22.join(homedir(), "AppData", "Roaming");
21955
- const shortcutPath = path22.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Rudder.lnk");
22148
+ const appData = process.env.APPDATA?.trim() || path23.join(homedir(), "AppData", "Roaming");
22149
+ const shortcutPath = path23.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Rudder.lnk");
21956
22150
  return [
21957
22151
  "$shell = New-Object -ComObject WScript.Shell",
21958
22152
  `$shortcut = $shell.CreateShortcut(${powershellQuote(shortcutPath)})`,
21959
22153
  `$shortcut.TargetPath = ${powershellQuote(executablePath)}`,
21960
- `$shortcut.WorkingDirectory = ${powershellQuote(path22.dirname(executablePath))}`,
22154
+ `$shortcut.WorkingDirectory = ${powershellQuote(path23.dirname(executablePath))}`,
21961
22155
  "$shortcut.Save()"
21962
22156
  ].join("; ");
21963
22157
  }
@@ -21989,7 +22183,7 @@ function launchDesktop(paths, target) {
21989
22183
  spawn3(paths.executablePath, [], { detached: true, stdio: "ignore" }).unref();
21990
22184
  }
21991
22185
  async function writeInstallMetadata(paths, releaseTag, assetName, assetChecksum, assetKind = "full") {
21992
- mkdirSync(path22.dirname(paths.metadataPath), { recursive: true });
22186
+ mkdirSync2(path23.dirname(paths.metadataPath), { recursive: true });
21993
22187
  const metadata = {
21994
22188
  version: 1,
21995
22189
  releaseTag,
@@ -21998,7 +22192,7 @@ async function writeInstallMetadata(paths, releaseTag, assetName, assetChecksum,
21998
22192
  assetKind,
21999
22193
  installedAt: (/* @__PURE__ */ new Date()).toISOString()
22000
22194
  };
22001
- mkdirSync(paths.installRoot, { recursive: true });
22195
+ mkdirSync2(paths.installRoot, { recursive: true });
22002
22196
  await writeFile3(paths.metadataPath, `${JSON.stringify(metadata, null, 2)}
22003
22197
  `, "utf8");
22004
22198
  }
@@ -22044,7 +22238,7 @@ async function startCommand(opts) {
22044
22238
  if (!exactDesktopAssetPath || !exactDesktopAssetChecksum || !exactDesktopAssetName || !exactDesktopReleaseDigest) {
22045
22239
  throw new Error("Exact Desktop asset mode requires path, checksum, asset name, and release digest.");
22046
22240
  }
22047
- if (!path22.isAbsolute(exactDesktopAssetPath) || exactDesktopAssetName.includes("/") || !/^[a-f0-9]{64}$/iu.test(exactDesktopAssetChecksum) || !/^[a-f0-9]{64}$/iu.test(exactDesktopReleaseDigest)) {
22241
+ if (!path23.isAbsolute(exactDesktopAssetPath) || exactDesktopAssetName.includes("/") || !/^[a-f0-9]{64}$/iu.test(exactDesktopAssetChecksum) || !/^[a-f0-9]{64}$/iu.test(exactDesktopReleaseDigest)) {
22048
22242
  throw new Error("Exact Desktop asset mode received invalid candidate identity.");
22049
22243
  }
22050
22244
  if (opts.desktopAssetKind && opts.desktopAssetKind !== "full" && opts.desktopAssetKind !== "shell") {
@@ -22122,15 +22316,18 @@ async function startCommand(opts) {
22122
22316
  }
22123
22317
  }
22124
22318
  if (installDesktop) {
22319
+ const downloadSource = resolveDesktopDownloadSource(opts.downloadSource);
22320
+ const mirrorBaseUrl = resolveDesktopReleaseMirrorBaseUrl(repo);
22125
22321
  const target = resolveDesktopAssetTarget();
22126
22322
  const tag = resolveDesktopReleaseTag(version);
22127
- const installRoot = opts.desktopInstallDir ? path22.resolve(opts.desktopInstallDir) : resolveDefaultDesktopInstallRoot(target);
22323
+ const installRoot = opts.desktopInstallDir ? path23.resolve(opts.desktopInstallDir) : resolveDefaultDesktopInstallRoot(target);
22128
22324
  const installPaths = resolveDesktopInstallPaths(target, installRoot);
22129
- const outputDir = opts.outputDir ? path22.resolve(opts.outputDir) : await mkdtemp2(path22.join(tmpdir(), "rudder-desktop-installer."));
22325
+ const outputDir = opts.outputDir ? path23.resolve(opts.outputDir) : await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
22130
22326
  p15.log.step("Installing desktop app");
22131
22327
  p15.log.message(`Release: ${pc14.cyan(`${repo}@${tag}`)}`);
22132
22328
  p15.log.message(`Target: ${pc14.cyan(`${target.platform}/${target.arch}`)}`);
22133
22329
  p15.log.message(`Install: ${pc14.cyan(installPaths.appPath)}`);
22330
+ p15.log.message(`Download source: ${pc14.cyan(downloadSource)}`);
22134
22331
  if (dryRun) {
22135
22332
  p15.log.message(`[dry-run] Would resolve, download, verify, install, and ${opts.open === false ? "not launch" : "launch"} Rudder Desktop.`);
22136
22333
  p15.outro(pc14.green("Dry run complete."));
@@ -22145,6 +22342,7 @@ async function startCommand(opts) {
22145
22342
  let cachedAsset = null;
22146
22343
  let assetCandidates = [];
22147
22344
  let checksums = /* @__PURE__ */ new Map();
22345
+ let downloadOrigins = ["github"];
22148
22346
  if (exactDesktopAssetPath) {
22149
22347
  releaseTag = tag;
22150
22348
  selectedAsset = {
@@ -22153,7 +22351,7 @@ async function startCommand(opts) {
22153
22351
  };
22154
22352
  selectedAssetKind = opts.desktopAssetKind ?? "full";
22155
22353
  expectedChecksum = normalizeDesktopAssetChecksum(exactDesktopAssetChecksum);
22156
- const computedReleaseDigest = createHash2("sha256").update(JSON.stringify({
22354
+ const computedReleaseDigest = createHash4("sha256").update(JSON.stringify({
22157
22355
  releaseTag,
22158
22356
  assetName: selectedAsset.name,
22159
22357
  assetChecksum: expectedChecksum,
@@ -22170,7 +22368,7 @@ async function startCommand(opts) {
22170
22368
  if (linkDescriptor.isSymbolicLink()) throw new Error("Exact Desktop asset must not be a symbolic link.");
22171
22369
  const checksum = await runStartPhase(
22172
22370
  "Verifying staged Desktop checksum...",
22173
- `Verified ${pc14.cyan(path22.basename(exactDesktopAssetPath))}.`,
22371
+ `Verified ${pc14.cyan(path23.basename(exactDesktopAssetPath))}.`,
22174
22372
  () => assertChecksumMatch(exactDesktopAssetPath, expectedChecksum),
22175
22373
  desktopProgressJson ? "verifying_checksum" : null
22176
22374
  );
@@ -22205,6 +22403,20 @@ async function startCommand(opts) {
22205
22403
  throw new Error(`No Rudder Desktop portable asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
22206
22404
  }
22207
22405
  const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(repo, tag, DESKTOP_CHECKSUM_ASSET_NAME) : null);
22406
+ if (!checksumAsset) {
22407
+ throw new Error("Desktop release is missing SHASUMS256.txt.");
22408
+ }
22409
+ downloadOrigins = await resolveDesktopDownloadOrigins({
22410
+ source: downloadSource,
22411
+ checksumAsset,
22412
+ tag: releaseTag,
22413
+ mirrorBaseUrl
22414
+ });
22415
+ if (downloadOrigins[0] === "mirror") {
22416
+ p15.log.message("Using the China release mirror with GitHub fallback.");
22417
+ } else if (downloadSource !== "global" && mirrorBaseUrl) {
22418
+ p15.log.message("Using GitHub for this network path.");
22419
+ }
22208
22420
  checksums = await downloadChecksums(checksumAsset, outputDir, progressFactory);
22209
22421
  let selectedCandidate;
22210
22422
  try {
@@ -22213,7 +22425,10 @@ async function startCommand(opts) {
22213
22425
  throw new Error(`No checksummed Rudder Desktop asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
22214
22426
  }
22215
22427
  for (const warning of selectedCandidate.warnings) p15.log.warn(warning);
22216
- selectedAsset = selectedCandidate.asset;
22428
+ selectedAsset = withDesktopDownloadOrigins(selectedCandidate.asset, downloadOrigins, {
22429
+ mirrorBaseUrl,
22430
+ tag: releaseTag
22431
+ });
22217
22432
  selectedAssetKind = selectedCandidate.kind;
22218
22433
  expectedChecksum = selectedCandidate.expectedChecksum;
22219
22434
  }
@@ -22241,7 +22456,10 @@ async function startCommand(opts) {
22241
22456
  p15.log.warn(
22242
22457
  `Layered Desktop shell asset download failed; falling back to the full portable asset. ${formatFetchError(error)}`
22243
22458
  );
22244
- selectedAsset = fullCandidate.asset;
22459
+ selectedAsset = withDesktopDownloadOrigins(fullCandidate.asset, downloadOrigins, {
22460
+ mirrorBaseUrl,
22461
+ tag: releaseTag
22462
+ });
22245
22463
  selectedAssetKind = fullCandidate.kind;
22246
22464
  expectedChecksum = resolveAssetChecksum(checksums, selectedAsset.name);
22247
22465
  cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
@@ -22263,7 +22481,7 @@ async function startCommand(opts) {
22263
22481
  }
22264
22482
  const checksum = await runStartPhase(
22265
22483
  "Verifying Desktop checksum...",
22266
- `Verified ${pc14.cyan(path22.basename(verifiedAsset.path))}.`,
22484
+ `Verified ${pc14.cyan(path23.basename(verifiedAsset.path))}.`,
22267
22485
  () => assertChecksumMatch(verifiedAsset.path, expectedChecksum),
22268
22486
  desktopProgressJson ? "verifying_checksum" : null
22269
22487
  );
@@ -22274,9 +22492,9 @@ async function startCommand(opts) {
22274
22492
  percent: 100,
22275
22493
  assetName: selectedAsset.name,
22276
22494
  assetChecksum: checksum,
22277
- stagedArtifactPath: path22.resolve(verifiedAsset.path),
22495
+ stagedArtifactPath: path23.resolve(verifiedAsset.path),
22278
22496
  stagedArtifactDigest: checksum,
22279
- releaseDigest: createHash2("sha256").update(JSON.stringify({
22497
+ releaseDigest: createHash4("sha256").update(JSON.stringify({
22280
22498
  releaseTag,
22281
22499
  assetName: selectedAsset.name,
22282
22500
  assetChecksum: checksum,
@@ -22367,11 +22585,11 @@ async function startCommand(opts) {
22367
22585
 
22368
22586
  // src/config/data-dir.ts
22369
22587
  init_home();
22370
- import path23 from "node:path";
22588
+ import path24 from "node:path";
22371
22589
  function applyDataDirOverride(options, support = {}) {
22372
22590
  const rawDataDir = options.dataDir?.trim();
22373
22591
  if (!rawDataDir) return null;
22374
- const resolvedDataDir = path23.resolve(expandHomePrefix(rawDataDir));
22592
+ const resolvedDataDir = path24.resolve(expandHomePrefix(rawDataDir));
22375
22593
  process.env.RUDDER_HOME = resolvedDataDir;
22376
22594
  if (support.hasConfigOption) {
22377
22595
  const hasConfigOverride = Boolean(options.config?.trim()) || Boolean(process.env.RUDDER_CONFIG?.trim());
@@ -22431,7 +22649,7 @@ function createProgram() {
22431
22649
  });
22432
22650
  loadRudderEnvFile(options.config);
22433
22651
  });
22434
- program.command("start").description("Start Rudder Desktop and prepare the matching persistent CLI").option("--server-only", "Prepare the Rudder server runtime and persistent CLI without installing Desktop").option("--no-cli", "Skip persistent CLI installation").option("--no-runtime", "Skip Rudder runtime installation").option("--no-desktop", "Skip desktop app installation").option("--version <version>", "Rudder version to start (default: current CLI version)").option("--target-version <version>", "Rudder version to start; avoids the root CLI version flag").option("--repo <owner/repo>", "GitHub repository that hosts desktop releases").option("--output-dir <path>", "Directory for downloaded desktop release assets").option("--desktop-install-dir <path>", "Directory for the portable Desktop install").option("--no-open", "Install Desktop without launching it").option("--wait-for-active-runs", "Wait for active Rudder runs to finish before replacing Desktop", false).option("--desktop-progress-json", "Emit newline-delimited Desktop update progress events").option("--desktop-wait-for-apply", "Wait for an apply signal after downloading and verifying the Desktop update", false).option("--desktop-prepare-only", "Download and verify the Desktop update without installing or launching it", false).option("--desktop-asset-path <path>", "Use one previously staged, exact Desktop asset path").option("--desktop-asset-checksum <sha256>", "SHA-256 for the exact staged Desktop asset").option("--desktop-asset-name <name>", "Asset name bound to the exact staged Desktop candidate").option("--desktop-asset-kind <kind>", "Asset kind bound to the exact staged Desktop candidate (full or shell)").option("--desktop-release-digest <sha256>", "Release digest bound to the exact staged Desktop candidate").option("--no-version-check", "Skip checking npm for a newer Rudder CLI version").option("--dry-run", "Print the start actions without changing the machine", false).action(startCommand);
22652
+ program.command("start").description("Start Rudder Desktop and prepare the matching persistent CLI").option("--server-only", "Prepare the Rudder server runtime and persistent CLI without installing Desktop").option("--no-cli", "Skip persistent CLI installation").option("--no-runtime", "Skip Rudder runtime installation").option("--no-desktop", "Skip desktop app installation").option("--version <version>", "Rudder version to start (default: current CLI version)").option("--target-version <version>", "Rudder version to start; avoids the root CLI version flag").option("--repo <owner/repo>", "GitHub repository that hosts desktop releases").option("--download-source <source>", "Desktop download source: auto, cn, or global").option("--output-dir <path>", "Directory for downloaded desktop release assets").option("--desktop-install-dir <path>", "Directory for the portable Desktop install").option("--no-open", "Install Desktop without launching it").option("--wait-for-active-runs", "Wait for active Rudder runs to finish before replacing Desktop", false).option("--desktop-progress-json", "Emit newline-delimited Desktop update progress events").option("--desktop-wait-for-apply", "Wait for an apply signal after downloading and verifying the Desktop update", false).option("--desktop-prepare-only", "Download and verify the Desktop update without installing or launching it", false).option("--desktop-asset-path <path>", "Use one previously staged, exact Desktop asset path").option("--desktop-asset-checksum <sha256>", "SHA-256 for the exact staged Desktop asset").option("--desktop-asset-name <name>", "Asset name bound to the exact staged Desktop candidate").option("--desktop-asset-kind <kind>", "Asset kind bound to the exact staged Desktop candidate (full or shell)").option("--desktop-release-digest <sha256>", "Release digest bound to the exact staged Desktop candidate").option("--no-version-check", "Skip checking npm for a newer Rudder CLI version").option("--dry-run", "Print the start actions without changing the machine", false).action(startCommand);
22435
22653
  program.command("onboard").description("Interactive first-run setup wizard").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("-y, --yes", "Accept defaults (quickstart + start immediately)", false).option("--run", "Start Rudder immediately after saving config", false).action(onboard);
22436
22654
  program.command("doctor").description("Run diagnostic checks on your Rudder setup").option("-c, --config <path>", "Path to config file").option("-d, --data-dir <path>", DATA_DIR_OPTION_HELP).option("--repair", "Attempt to repair issues automatically").alias("--fix").option("-y, --yes", "Skip repair confirmation prompts").action(async (opts) => {
22437
22655
  await doctor(opts);