@rudderhq/cli 0.3.5-canary.2 → 0.3.5-canary.20

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
@@ -768,7 +768,7 @@ var init_adapter_skills = __esm({
768
768
 
769
769
  // ../packages/shared/dist/validators/chat.js
770
770
  import { z as z4 } from "zod";
771
- var chatConversationStatusSchema, chatIssueCreationModeSchema, chatMessageRoleSchema, chatMessageKindSchema, chatMessageStatusSchema, chatContextEntityTypeSchema, createChatContextLinkSchema, createChatConversationSchema, setChatProjectContextSchema, updateChatConversationSchema, addChatMessageSchema, chatRichReferenceDisplaySchema, chatIssueIdentifierSchema, chatAskUserIdentifierSchema, chatAskUserOptionSchema, chatAskUserQuestionSchema, chatAskUserRequestSchema, chatIssueRichReferenceSchema, chatIssueCommentRichReferenceSchema, chatRichReferenceSchema, chatRichReferencesSchema, chatAutomationCreateSchema, createChatAttachmentMetadataSchema, chatIssueProposalSchema, convertChatToIssueSchema, chatOperationProposalSchema, resolveChatOperationProposalSchema, updateChatConversationUserStateSchema, updateMessengerThreadUserStateSchema;
771
+ var chatConversationStatusSchema, chatIssueCreationModeSchema, chatMessageRoleSchema, chatMessageKindSchema, chatMessageStatusSchema, chatContextEntityTypeSchema, createChatContextLinkSchema, createChatConversationSchema, setChatProjectContextSchema, updateChatConversationSchema, addChatMessageSchema, chatRichReferenceDisplaySchema, chatIssueIdentifierSchema, chatAskUserIdentifierSchema, chatAskUserOptionSchema, chatAskUserQuestionSchema, chatAskUserRequestSchema, chatIssueRichReferenceSchema, chatIssueCommentRichReferenceSchema, chatRichReferenceSchema, chatRichReferencesSchema, chatAutomationCreateSchema, createChatAttachmentMetadataSchema, chatIssueProposalSchema, convertChatToIssueSchema, chatOperationProposalSchema, resolveChatOperationProposalSchema, updateChatConversationUserStateSchema, updateMessengerThreadUserStateSchema, createMessengerCustomGroupSchema, updateMessengerCustomGroupSchema, reorderMessengerCustomGroupsSchema, assignMessengerCustomGroupEntrySchema, reorderMessengerCustomGroupEntriesSchema;
772
772
  var init_chat = __esm({
773
773
  "../packages/shared/dist/validators/chat.js"() {
774
774
  "use strict";
@@ -951,6 +951,25 @@ var init_chat = __esm({
951
951
  updateMessengerThreadUserStateSchema = z4.object({
952
952
  pinned: z4.boolean().optional()
953
953
  });
954
+ createMessengerCustomGroupSchema = z4.object({
955
+ name: z4.string().trim().min(1).max(80),
956
+ icon: z4.string().trim().min(1).max(24).optional().nullable()
957
+ });
958
+ updateMessengerCustomGroupSchema = z4.object({
959
+ name: z4.string().trim().min(1).max(80).optional(),
960
+ icon: z4.string().trim().min(1).max(24).optional().nullable(),
961
+ collapsed: z4.boolean().optional(),
962
+ sortOrder: z4.number().int().min(0).optional()
963
+ });
964
+ reorderMessengerCustomGroupsSchema = z4.object({
965
+ groupIds: z4.array(z4.string().uuid()).max(200)
966
+ });
967
+ assignMessengerCustomGroupEntrySchema = z4.object({
968
+ threadKey: z4.string().trim().min(1).max(240)
969
+ });
970
+ reorderMessengerCustomGroupEntriesSchema = z4.object({
971
+ threadKeys: z4.array(z4.string().trim().min(1).max(240)).max(500)
972
+ });
954
973
  }
955
974
  });
956
975
 
@@ -7054,15 +7073,6 @@ var RUDDER_AGENT_OPERATING_CONTRACT = [
7054
7073
  "",
7055
7074
  "When you create or copy a skill under `$AGENT_HOME/skills/<slug>/`, check the agent's Skills snapshot before claiming it will load in future runs. If it is installed but not enabled, say exactly that future runs will not load it until enabled, and offer to enable it with `rudder agent skills enable <agent-id> <selection-ref>` when you have permission.",
7056
7075
  "",
7057
- "## Self-Improvement and Automation Requests",
7058
- "",
7059
- "When the user asks to improve, optimize, or remember a workflow, treat it as a request to update the durable operating surface that controls future behavior, not as a request for advice only. First identify the surface: an automation, an agent-private skill, an organization skill, memory, instructions, or a project Library document. Then inspect the existing surface and apply the smallest governed update you are authorized to make.",
7060
- "",
7061
- "- For automation behavior, read the current automation or chat context first, then use the Rudder automation commands such as `rudder automation list`, `rudder automation get`, and `rudder automation update` instead of only describing what should change.",
7062
- "- For skill improvement, find real evidence before editing: use `rudder runs by-skill <skill> --org-id <id>` or `rudder runs list --used-skill <skill>` to locate runs, then inspect the relevant transcripts or errors with `rudder runs transcript` / `rudder runs errors`.",
7063
- "- For personal memory, use the `para-memory-files` skill; for agent-private skills, prefer `rudder agent skills create ... --enable` or editing `$AGENT_HOME/skills`; for organization skills, follow the bundled `rudder` skill's organization-skill workflow.",
7064
- "- Do not silently mutate broad shared behavior when the request is ambiguous, risky, or outside your authority. Make the proposed change reviewable, ask for approval when needed, and report exactly which durable surface changed.",
7065
- "",
7066
7076
  "When you write issue comments or chat replies, match the language of the user's or board's most recent substantive message unless they explicitly ask for a different language.",
7067
7077
  "When you mention a web page, issue URL, external dashboard, or other user-openable target in an issue comment or chat reply, write it as a clickable Markdown link with a descriptive label, for example `[NameSilo transfer page](https://www.namesilo.com/account_domain_manage_transfer.php)`. Do not put action URLs in backticks or code blocks unless you are showing literal code or a command.",
7068
7078
  "",
@@ -7070,16 +7080,16 @@ var RUDDER_AGENT_OPERATING_CONTRACT = [
7070
7080
  "",
7071
7081
  "When you mention Rudder entities in any user-visible Markdown output, prefer Rudder's renderable Markdown link syntax over plain IDs, bare URLs, or backticked references so the UI can render chips and navigate correctly.",
7072
7082
  "",
7073
- "- Issues: use `[<identifier-or-title>](issue://<issue-id>)`; include `?c=<comment-id>` when linking to a specific comment.",
7074
- "- Agents: use `[<agent-name>](agent://<agent-id>)` for reference-only links. In issue comments, use `[<agent-name>](agent://<agent-id>?intent=wake)` only when you intentionally want to wake that agent for attention or collaboration.",
7075
- "- Projects: use `[<project-name>](project://<project-id>)` when citing a Rudder project.",
7076
- "- Chat threads: use `[<chat-title>](chat://<conversation-id>)` when citing a Rudder chat conversation.",
7077
- "- Skills: use `[$<skill-ref>](<path-or-url-ending-in-SKILL.md>)` when citing a Rudder skill reference; the label may use `org/<org-url-key>/<skill-slug>`, `org/<org-url-key>/<agent-url-key>/<skill-slug>`, or `rudder/<skill-slug>`.",
7083
+ "- Issues: use `[](issue://<issue-id>)`; include `?c=<comment-id>` when linking to a specific comment.",
7084
+ "- Agents: use `[](agent://<agent-id>)` for reference-only links. In issue comments, use `[](agent://<agent-id>?intent=wake)` only when you intentionally want to wake that agent for attention or collaboration.",
7085
+ "- Projects: use `[](project://<project-id>)` when citing a Rudder project.",
7086
+ "- Chat threads: use `[](chat://<conversation-id>)` when citing a Rudder chat conversation.",
7087
+ "- Skills: use `[](skill://<skill-ref>)` when citing a Rudder skill reference. The skill ref may be an org skill, agent skill, bundled Rudder skill, or local-machine skill ref; the UI resolves the display label when metadata is available.",
7078
7088
  '- Library files: use the `markdownLink` returned by `rudder library file ref "$RUDDER_PROJECT_LIBRARY_PATH/<relative-file>" --json`; do not hand-write `library-entry://...` links, and treat `library-file://...` as legacy path syntax only.',
7079
7089
  "",
7080
7090
  "Write these as normal Markdown links, not inside code spans or code blocks, unless you are literally documenting the syntax.",
7081
7091
  "",
7082
- "Mention-triggered comment wakes arrive with `RUDDER_WAKE_COMMENT_ID`; read that wake comment before acting. Issue comments can wake an agent only with an explicit wake-intent agent link serialized as `agent://agent-id?intent=wake`. Plain structured links such as `agent://agent-id` are reference-only links for rendering and navigation, and plain text agent names are not wake requests. Use wake-intent links only when you intentionally want to wake another agent for attention or collaboration; omit the wake intent for ordinary references. Mentioning an agent requests attention or collaboration; it does not transfer issue ownership unless the comment also makes an explicit handoff and normal checkout rules allow it.",
7092
+ "Mention-triggered comment wakes arrive with `RUDDER_WAKE_COMMENT_ID`; read that wake comment before acting. Issue comments can wake an agent only with an explicit wake-intent agent link serialized as `agent://agent-id?intent=wake`. Plain structured links such as `agent://agent-id` are reference-only links for rendering and navigation, and plain text agent names are not wake requests. Use wake-intent links only when you intentionally want to wake another agent for attention or collaboration; omit the wake intent for ordinary references. Mentioning an agent requests attention or collaboration; it does not transfer issue ownership unless the comment also makes an explicit handoff and normal checkout rules allow it. If a comment wakes you on an issue not assigned to you, including user-owned or unassigned issues, and the comment does not explicitly ask you to implement, modify files, close the issue, or take ownership, strictly respond to the comment's content instead of turning the wake into issue execution; answer questions, acknowledge corrections, explain status, or handle only the narrow action the comment explicitly requests.",
7083
7093
  "",
7084
7094
  "When an issue comment, done comment, or blocker comment cites visual evidence from a local screenshot/image path, attach the image with the Rudder CLI `--image <path>` option instead of leaving only the filesystem path in the text.",
7085
7095
  "",
@@ -7111,7 +7121,7 @@ var RUDDER_AGENT_HEARTBEAT_INSTRUCTION = [
7111
7121
  "1. Identify yourself and inspect wake context, including `RUDDER_TASK_ID`, `RUDDER_WAKE_REASON`, `RUDDER_WAKE_COMMENT_ID`, and `RUDDER_APPROVAL_ID` when present.",
7112
7122
  "2. Handle approval follow-up first: read the approval and linked issues, then close resolved work or comment on what remains.",
7113
7123
  "3. Inspect your Rudder inbox. Prioritize reviewer rows in `in_review` or `blocked`, then assignee `in_progress`, then assignee `todo`. Do not look for unassigned work.",
7114
- "4. For mention wakes, read the wake comment before acting. Mentions request attention; they do not transfer ownership unless the comment explicitly says so.",
7124
+ "4. For mention wakes, read the wake comment before acting. Mentions request attention; they do not transfer ownership unless the comment explicitly says so. If the issue is not assigned to you, including user-owned or unassigned issues, and the comment does not explicitly ask you to implement, modify files, close the issue, or take ownership, respond to the comment itself instead of executing the whole issue.",
7115
7125
  "5. Checkout before doing assignee task work. A `409` means another agent owns the task; do not retry it.",
7116
7126
  "6. Load compact issue context, do one bounded useful chunk, and preserve evidence.",
7117
7127
  "7. Before exiting active work, leave exactly one durable signal: progress, done, blocked, explicit handoff, or structured review decision.",
@@ -9489,6 +9499,11 @@ function formatChatMessage(row, maxOutputChars = 1200) {
9489
9499
  role: row.role,
9490
9500
  kind: row.kind,
9491
9501
  status: row.status,
9502
+ ...row.runId ? {
9503
+ runId: row.runId,
9504
+ runCommand: `rudder runs get ${row.runId}`,
9505
+ transcriptCommand: `rudder runs transcript ${row.runId}`
9506
+ } : {},
9492
9507
  createdAt: row.createdAt,
9493
9508
  body: clip(row.body, 220),
9494
9509
  transcriptEntries: row.transcriptSummary?.entryCount ?? row.transcript?.length ?? 0,
@@ -9501,6 +9516,11 @@ function formatChatTranscriptMessage(row, maxChars) {
9501
9516
  role: row.role,
9502
9517
  kind: row.kind,
9503
9518
  status: row.status,
9519
+ ...row.runId ? {
9520
+ runId: row.runId,
9521
+ runCommand: `rudder runs get ${row.runId}`,
9522
+ transcriptCommand: `rudder runs transcript ${row.runId}`
9523
+ } : {},
9504
9524
  createdAt: row.createdAt,
9505
9525
  body: clip(row.body, 220)
9506
9526
  };
@@ -13035,7 +13055,7 @@ init_install2();
13035
13055
  init_install();
13036
13056
  import * as p15 from "@clack/prompts";
13037
13057
  import { spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
13038
- import { createHash } from "node:crypto";
13058
+ import { createHash, randomUUID } from "node:crypto";
13039
13059
  import { createWriteStream, constants as fsConstants, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
13040
13060
  import { access, chmod, copyFile, cp, mkdir as mkdir3, mkdtemp, readdir as readdir3, readFile as readFile6, rm as rm2, stat as stat4, utimes, writeFile as writeFile3 } from "node:fs/promises";
13041
13061
  import { homedir, tmpdir } from "node:os";
@@ -13171,6 +13191,8 @@ var DEFAULT_DESKTOP_ASSET_CACHE_MAX_ENTRIES = 2;
13171
13191
  var DEFAULT_DESKTOP_ASSET_CACHE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
13172
13192
  var DEFAULT_DESKTOP_ASSET_CACHE_MAX_BYTES = 768 * 1024 * 1024;
13173
13193
  var DEFAULT_DESKTOP_ASSET_CACHE_KEEP_PREVIOUS = 1;
13194
+ var DESKTOP_INSTALL_LOCK_TIMEOUT_MS = 60 * 60 * 1e3;
13195
+ var DESKTOP_INSTALL_LOCK_POLL_MS = 250;
13174
13196
  function normalizeProgressTotal(totalBytes) {
13175
13197
  return typeof totalBytes === "number" && Number.isFinite(totalBytes) && totalBytes > 0 ? totalBytes : null;
13176
13198
  }
@@ -13814,6 +13836,70 @@ async function pathExists2(targetPath) {
13814
13836
  return false;
13815
13837
  }
13816
13838
  }
13839
+ function resolveDesktopInstallLockPath(paths) {
13840
+ const installRootHash = createHash("sha256").update(path21.resolve(paths.installRoot)).digest("hex").slice(0, 16);
13841
+ return path21.join(path21.dirname(paths.appPath), `.rudder-desktop-install-${installRootHash}.lock`);
13842
+ }
13843
+ async function readDesktopInstallLock(lockPath) {
13844
+ try {
13845
+ const parsed = JSON.parse(await readFile6(lockPath, "utf8"));
13846
+ if (typeof parsed.pid !== "number" || !Number.isInteger(parsed.pid) || parsed.pid <= 0 || typeof parsed.lockId !== "string" || typeof parsed.installRoot !== "string" || typeof parsed.createdAt !== "string") {
13847
+ return null;
13848
+ }
13849
+ return {
13850
+ lockId: parsed.lockId,
13851
+ pid: parsed.pid,
13852
+ installRoot: parsed.installRoot,
13853
+ createdAt: parsed.createdAt
13854
+ };
13855
+ } catch {
13856
+ return null;
13857
+ }
13858
+ }
13859
+ async function withDesktopInstallLock(paths, fn, options = {}) {
13860
+ const lockPath = resolveDesktopInstallLockPath(paths);
13861
+ const lockDir = path21.dirname(lockPath);
13862
+ const timeoutMs = options.timeoutMs ?? DESKTOP_INSTALL_LOCK_TIMEOUT_MS;
13863
+ const pollMs = options.pollMs ?? DESKTOP_INSTALL_LOCK_POLL_MS;
13864
+ const startedAt = Date.now();
13865
+ const payload = {
13866
+ lockId: randomUUID(),
13867
+ pid: process.pid,
13868
+ installRoot: path21.resolve(paths.installRoot),
13869
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
13870
+ };
13871
+ await mkdir3(lockDir, { recursive: true });
13872
+ while (true) {
13873
+ try {
13874
+ await writeFile3(lockPath, `${JSON.stringify(payload, null, 2)}
13875
+ `, { encoding: "utf8", flag: "wx" });
13876
+ break;
13877
+ } catch (error) {
13878
+ const err = error;
13879
+ if (err.code !== "EEXIST") throw error;
13880
+ const existing = await readDesktopInstallLock(lockPath);
13881
+ const stale = !existing || !processExists(existing.pid);
13882
+ if (stale) {
13883
+ await rm2(lockPath, { force: true });
13884
+ continue;
13885
+ }
13886
+ if (Date.now() - startedAt >= timeoutMs) {
13887
+ throw new Error(
13888
+ `Timed out waiting for Rudder Desktop install lock for ${paths.appPath}. Held by pid ${existing.pid} for ${existing.installRoot}.`
13889
+ );
13890
+ }
13891
+ await delay2(pollMs);
13892
+ }
13893
+ }
13894
+ try {
13895
+ return await fn();
13896
+ } finally {
13897
+ const existing = await readDesktopInstallLock(lockPath);
13898
+ if (existing?.lockId === payload.lockId) {
13899
+ await rm2(lockPath, { force: true });
13900
+ }
13901
+ }
13902
+ }
13817
13903
  function runChecked(command, args, options = {}) {
13818
13904
  const result = spawnSync3(command, args, {
13819
13905
  encoding: "utf8",
@@ -14266,152 +14352,154 @@ async function startCommand(opts) {
14266
14352
  p15.outro(pc15.green("Dry run complete."));
14267
14353
  return;
14268
14354
  }
14269
- const directReleaseVersion = resolveDesktopReleaseVersion(tag);
14270
- const progressFactory = desktopProgressJson ? createDesktopProgressFactory() : createByteProgress;
14271
- let release = null;
14272
- try {
14273
- release = await runStartPhase(
14274
- "Resolving Desktop release...",
14275
- "Desktop release resolved.",
14276
- () => fetchGithubRelease(repo, tag),
14277
- desktopProgressJson ? "resolving_release" : null
14278
- );
14279
- } catch (error) {
14280
- if (!directReleaseVersion) throw error;
14281
- p15.log.warn(
14282
- `Desktop release metadata could not be resolved; falling back to deterministic download URLs. ${formatFetchError(error)}`
14283
- );
14284
- }
14285
- const releaseTag = release?.tag_name ?? (directReleaseVersion ? tag : null);
14286
- if (!releaseTag) {
14287
- throw new Error(`Unable to resolve Rudder Desktop release tag for ${repo}@${tag}.`);
14288
- }
14289
- const assetCandidates = resolveDesktopAssetCandidates({
14290
- releaseAssets: release?.assets ?? [],
14291
- target,
14292
- repo,
14293
- tag,
14294
- directReleaseVersion,
14295
- allowShellAssets: runtimeSupportsShellAssets
14296
- });
14297
- if (assetCandidates.length === 0) {
14298
- throw new Error(`No Rudder Desktop portable asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
14299
- }
14300
- const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(repo, tag, DESKTOP_CHECKSUM_ASSET_NAME) : null);
14301
- const checksums = await downloadChecksums(checksumAsset, outputDir, progressFactory);
14302
- let selectedCandidate;
14303
- try {
14304
- selectedCandidate = selectChecksummedDesktopAssetCandidate(assetCandidates, checksums);
14305
- } catch (error) {
14306
- throw new Error(`No checksummed Rudder Desktop asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
14307
- }
14308
- for (const warning of selectedCandidate.warnings) p15.log.warn(warning);
14309
- let selectedAsset = selectedCandidate.asset;
14310
- let selectedAssetKind = selectedCandidate.kind;
14311
- let expectedChecksum = selectedCandidate.expectedChecksum;
14312
- const metadata = await readInstallMetadata(installPaths.metadataPath);
14313
- if (isInstalledDesktopCurrent(metadata, releaseTag, selectedAsset.name, expectedChecksum) && await pathExists2(installPaths.executablePath)) {
14314
- p15.log.success(`Rudder Desktop is already installed at ${pc15.cyan(installPaths.appPath)}.`);
14315
- await runStartPhase(
14316
- "Refreshing Desktop launchers...",
14317
- "Desktop launchers ready.",
14318
- async () => {
14319
- await removeMacQuarantine(installPaths, target);
14320
- await createPlatformLaunchers(installPaths, target);
14321
- },
14322
- desktopProgressJson ? "preparing_restart" : null
14323
- );
14324
- } else {
14325
- let cachedAsset;
14355
+ await withDesktopInstallLock(installPaths, async () => {
14356
+ const directReleaseVersion = resolveDesktopReleaseVersion(tag);
14357
+ const progressFactory = desktopProgressJson ? createDesktopProgressFactory() : createByteProgress;
14358
+ let release = null;
14326
14359
  try {
14327
- cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
14328
- outputDir,
14329
- progressFactory
14330
- });
14360
+ release = await runStartPhase(
14361
+ "Resolving Desktop release...",
14362
+ "Desktop release resolved.",
14363
+ () => fetchGithubRelease(repo, tag),
14364
+ desktopProgressJson ? "resolving_release" : null
14365
+ );
14331
14366
  } catch (error) {
14332
- const fullCandidate = assetCandidates.find((candidate) => candidate.kind === "full");
14333
- if (selectedAssetKind !== "shell" || !fullCandidate) throw error;
14367
+ if (!directReleaseVersion) throw error;
14334
14368
  p15.log.warn(
14335
- `Layered Desktop shell asset download failed; falling back to the full portable asset. ${formatFetchError(error)}`
14369
+ `Desktop release metadata could not be resolved; falling back to deterministic download URLs. ${formatFetchError(error)}`
14336
14370
  );
14337
- selectedAsset = fullCandidate.asset;
14338
- selectedAssetKind = fullCandidate.kind;
14339
- expectedChecksum = resolveAssetChecksum(checksums, selectedAsset.name);
14340
- cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
14341
- outputDir,
14342
- progressFactory
14343
- });
14344
14371
  }
14345
- if (cachedAsset.cacheStatus === "hit") {
14346
- p15.log.success(`Desktop asset cache hit at ${pc15.cyan(cachedAsset.path)}.`);
14347
- if (desktopProgressJson) {
14372
+ const releaseTag = release?.tag_name ?? (directReleaseVersion ? tag : null);
14373
+ if (!releaseTag) {
14374
+ throw new Error(`Unable to resolve Rudder Desktop release tag for ${repo}@${tag}.`);
14375
+ }
14376
+ const assetCandidates = resolveDesktopAssetCandidates({
14377
+ releaseAssets: release?.assets ?? [],
14378
+ target,
14379
+ repo,
14380
+ tag,
14381
+ directReleaseVersion,
14382
+ allowShellAssets: runtimeSupportsShellAssets
14383
+ });
14384
+ if (assetCandidates.length === 0) {
14385
+ throw new Error(`No Rudder Desktop portable asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
14386
+ }
14387
+ const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(repo, tag, DESKTOP_CHECKSUM_ASSET_NAME) : null);
14388
+ const checksums = await downloadChecksums(checksumAsset, outputDir, progressFactory);
14389
+ let selectedCandidate;
14390
+ try {
14391
+ selectedCandidate = selectChecksummedDesktopAssetCandidate(assetCandidates, checksums);
14392
+ } catch (error) {
14393
+ throw new Error(`No checksummed Rudder Desktop asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
14394
+ }
14395
+ for (const warning of selectedCandidate.warnings) p15.log.warn(warning);
14396
+ let selectedAsset = selectedCandidate.asset;
14397
+ let selectedAssetKind = selectedCandidate.kind;
14398
+ let expectedChecksum = selectedCandidate.expectedChecksum;
14399
+ const metadata = await readInstallMetadata(installPaths.metadataPath);
14400
+ if (isInstalledDesktopCurrent(metadata, releaseTag, selectedAsset.name, expectedChecksum) && await pathExists2(installPaths.executablePath)) {
14401
+ p15.log.success(`Rudder Desktop is already installed at ${pc15.cyan(installPaths.appPath)}.`);
14402
+ await runStartPhase(
14403
+ "Refreshing Desktop launchers...",
14404
+ "Desktop launchers ready.",
14405
+ async () => {
14406
+ await removeMacQuarantine(installPaths, target);
14407
+ await createPlatformLaunchers(installPaths, target);
14408
+ },
14409
+ desktopProgressJson ? "preparing_restart" : null
14410
+ );
14411
+ } else {
14412
+ let cachedAsset;
14413
+ try {
14414
+ cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
14415
+ outputDir,
14416
+ progressFactory
14417
+ });
14418
+ } catch (error) {
14419
+ const fullCandidate = assetCandidates.find((candidate) => candidate.kind === "full");
14420
+ if (selectedAssetKind !== "shell" || !fullCandidate) throw error;
14421
+ p15.log.warn(
14422
+ `Layered Desktop shell asset download failed; falling back to the full portable asset. ${formatFetchError(error)}`
14423
+ );
14424
+ selectedAsset = fullCandidate.asset;
14425
+ selectedAssetKind = fullCandidate.kind;
14426
+ expectedChecksum = resolveAssetChecksum(checksums, selectedAsset.name);
14427
+ cachedAsset = await downloadDesktopAssetWithCache(selectedAsset, expectedChecksum, {
14428
+ outputDir,
14429
+ progressFactory
14430
+ });
14431
+ }
14432
+ if (cachedAsset.cacheStatus === "hit") {
14433
+ p15.log.success(`Desktop asset cache hit at ${pc15.cyan(cachedAsset.path)}.`);
14434
+ if (desktopProgressJson) {
14435
+ writeDesktopProgress({
14436
+ phase: "downloading_asset",
14437
+ message: `Desktop asset cache hit for ${selectedAsset.name}.`,
14438
+ percent: 100
14439
+ });
14440
+ }
14441
+ }
14442
+ const checksum = await runStartPhase(
14443
+ "Verifying Desktop checksum...",
14444
+ `Verified ${pc15.cyan(path21.basename(cachedAsset.path))}.`,
14445
+ () => assertChecksumMatch(cachedAsset.path, expectedChecksum),
14446
+ desktopProgressJson ? "verifying_checksum" : null
14447
+ );
14448
+ if (desktopProgressJson && opts.desktopWaitForApply === true) {
14348
14449
  writeDesktopProgress({
14349
- phase: "downloading_asset",
14350
- message: `Desktop asset cache hit for ${selectedAsset.name}.`,
14450
+ phase: "ready_to_install",
14451
+ message: "Desktop update is downloaded and verified.",
14351
14452
  percent: 100
14352
14453
  });
14454
+ await waitForDesktopApplySignal();
14455
+ writeDesktopProgress({
14456
+ phase: "preparing_restart",
14457
+ message: "Applying Desktop update..."
14458
+ });
14353
14459
  }
14460
+ await runStartPhase(
14461
+ "Replacing existing Rudder Desktop if needed...",
14462
+ "Existing Desktop install is ready for replacement.",
14463
+ () => prepareForDesktopReplace(installPaths, target, { waitForActiveRuns: opts.waitForActiveRuns === true }),
14464
+ desktopProgressJson ? opts.waitForActiveRuns === true ? "waiting_for_active_runs" : "preparing_restart" : null
14465
+ );
14466
+ await runStartPhase(
14467
+ "Installing portable Desktop app...",
14468
+ `Installed Rudder Desktop to ${pc15.cyan(installPaths.appPath)}.`,
14469
+ () => installPortableDesktop(cachedAsset.path, installPaths, target),
14470
+ desktopProgressJson ? "preparing_restart" : null
14471
+ );
14472
+ await runStartPhase(
14473
+ "Preparing Desktop launchers...",
14474
+ "Desktop launchers ready.",
14475
+ async () => {
14476
+ await removeMacQuarantine(installPaths, target);
14477
+ await createPlatformLaunchers(installPaths, target);
14478
+ },
14479
+ desktopProgressJson ? "preparing_restart" : null
14480
+ );
14481
+ await writeInstallMetadata(installPaths, releaseTag, selectedAsset.name, checksum, selectedAssetKind);
14354
14482
  }
14355
- const checksum = await runStartPhase(
14356
- "Verifying Desktop checksum...",
14357
- `Verified ${pc15.cyan(path21.basename(cachedAsset.path))}.`,
14358
- () => assertChecksumMatch(cachedAsset.path, expectedChecksum),
14359
- desktopProgressJson ? "verifying_checksum" : null
14360
- );
14361
- if (desktopProgressJson && opts.desktopWaitForApply === true) {
14362
- writeDesktopProgress({
14363
- phase: "ready_to_install",
14364
- message: "Desktop update is downloaded and verified.",
14365
- percent: 100
14366
- });
14367
- await waitForDesktopApplySignal();
14368
- writeDesktopProgress({
14369
- phase: "preparing_restart",
14370
- message: "Applying Desktop update..."
14371
- });
14372
- }
14373
- await runStartPhase(
14374
- "Replacing existing Rudder Desktop if needed...",
14375
- "Existing Desktop install is ready for replacement.",
14376
- () => prepareForDesktopReplace(installPaths, target, { waitForActiveRuns: opts.waitForActiveRuns === true }),
14377
- desktopProgressJson ? opts.waitForActiveRuns === true ? "waiting_for_active_runs" : "preparing_restart" : null
14378
- );
14379
- await runStartPhase(
14380
- "Installing portable Desktop app...",
14381
- `Installed Rudder Desktop to ${pc15.cyan(installPaths.appPath)}.`,
14382
- () => installPortableDesktop(cachedAsset.path, installPaths, target),
14383
- desktopProgressJson ? "preparing_restart" : null
14384
- );
14385
- await runStartPhase(
14386
- "Preparing Desktop launchers...",
14387
- "Desktop launchers ready.",
14388
- async () => {
14389
- await removeMacQuarantine(installPaths, target);
14390
- await createPlatformLaunchers(installPaths, target);
14391
- },
14392
- desktopProgressJson ? "preparing_restart" : null
14393
- );
14394
- await writeInstallMetadata(installPaths, releaseTag, selectedAsset.name, checksum, selectedAssetKind);
14395
- }
14396
- const desktopAssetPrune = await maybePruneDesktopAssetCache({
14397
- protectedChecksums: [expectedChecksum]
14398
- });
14399
- if (desktopAssetPrune) {
14400
- if (desktopAssetPrune.deleted.length > 0) {
14401
- p15.log.success(
14402
- `Pruned ${desktopAssetPrune.deleted.length} old Desktop asset cache(s), freed ${formatBytes(desktopAssetPrune.freedBytes)}.`
14483
+ const desktopAssetPrune = await maybePruneDesktopAssetCache({
14484
+ protectedChecksums: [expectedChecksum]
14485
+ });
14486
+ if (desktopAssetPrune) {
14487
+ if (desktopAssetPrune.deleted.length > 0) {
14488
+ p15.log.success(
14489
+ `Pruned ${desktopAssetPrune.deleted.length} old Desktop asset cache(s), freed ${formatBytes(desktopAssetPrune.freedBytes)}.`
14490
+ );
14491
+ }
14492
+ for (const warning of desktopAssetPrune.warnings) p15.log.warn(warning);
14493
+ }
14494
+ if (opts.open !== false) {
14495
+ await runStartPhase(
14496
+ "Launching Rudder Desktop...",
14497
+ "Rudder Desktop launched.",
14498
+ () => launchDesktop(installPaths, target),
14499
+ desktopProgressJson ? "closing" : null
14403
14500
  );
14404
14501
  }
14405
- for (const warning of desktopAssetPrune.warnings) p15.log.warn(warning);
14406
- }
14407
- if (opts.open !== false) {
14408
- await runStartPhase(
14409
- "Launching Rudder Desktop...",
14410
- "Rudder Desktop launched.",
14411
- () => launchDesktop(installPaths, target),
14412
- desktopProgressJson ? "closing" : null
14413
- );
14414
- }
14502
+ });
14415
14503
  }
14416
14504
  p15.outro(pc15.green("Rudder start complete."));
14417
14505
  }