@rudderhq/cli 0.2.2-canary.9 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ var __export = (target, all) => {
10
10
  };
11
11
 
12
12
  // ../packages/shared/src/constants.ts
13
- var ORGANIZATION_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, AGENT_STATUSES, AGENT_RUNTIME_TYPES, AGENT_ROLES, AGENT_ICON_NAMES, ISSUE_STATUSES, ISSUE_PRIORITIES, CALENDAR_SOURCE_TYPES, CALENDAR_OWNER_TYPES, CALENDAR_VISIBILITIES, CALENDAR_SOURCE_STATUSES, CALENDAR_EVENT_KINDS, CALENDAR_EVENT_STATUSES, CALENDAR_SOURCE_MODES, CHAT_CONVERSATION_STATUSES, CHAT_ISSUE_CREATION_MODES, CHAT_MESSAGE_ROLES, CHAT_MESSAGE_KINDS, CHAT_MESSAGE_STATUSES, CHAT_CONTEXT_ENTITY_TYPES, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, ORGANIZATION_RESOURCE_KINDS, PROJECT_RESOURCE_ATTACHMENT_ROLES, AUTOMATION_STATUSES, AUTOMATION_CONCURRENCY_POLICIES, AUTOMATION_CATCH_UP_POLICIES, AUTOMATION_TRIGGER_SIGNING_MODES, PROJECT_COLORS, APPROVAL_TYPES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_INCIDENT_RESOLUTION_ACTIONS, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS;
13
+ var ORGANIZATION_STATUSES, DEPLOYMENT_MODES, DEPLOYMENT_EXPOSURES, AUTH_BASE_URL_MODES, AGENT_STATUSES, AGENT_RUNTIME_TYPES, AGENT_ROLES, AGENT_ICON_NAMES, AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX, AGENT_AVATAR_BACKGROUND_PRESET_IDS, ISSUE_STATUSES, ISSUE_PRIORITIES, CALENDAR_SOURCE_TYPES, CALENDAR_OWNER_TYPES, CALENDAR_VISIBILITIES, CALENDAR_SOURCE_STATUSES, CALENDAR_EVENT_KINDS, CALENDAR_EVENT_STATUSES, CALENDAR_SOURCE_MODES, CHAT_CONVERSATION_STATUSES, CHAT_ISSUE_CREATION_MODES, CHAT_MESSAGE_ROLES, CHAT_MESSAGE_KINDS, CHAT_MESSAGE_STATUSES, CHAT_CONTEXT_ENTITY_TYPES, GOAL_LEVELS, GOAL_STATUSES, PROJECT_STATUSES, ORGANIZATION_RESOURCE_KINDS, PROJECT_RESOURCE_ATTACHMENT_ROLES, AUTOMATION_STATUSES, AUTOMATION_CONCURRENCY_POLICIES, AUTOMATION_CATCH_UP_POLICIES, AUTOMATION_TRIGGER_SIGNING_MODES, PROJECT_COLORS, APPROVAL_TYPES, SECRET_PROVIDERS, STORAGE_PROVIDERS, BILLING_TYPES, FINANCE_EVENT_KINDS, FINANCE_DIRECTIONS, FINANCE_UNITS, BUDGET_SCOPE_TYPES, BUDGET_METRICS, BUDGET_WINDOW_KINDS, BUDGET_INCIDENT_RESOLUTION_ACTIONS, INVITE_JOIN_TYPES, JOIN_REQUEST_TYPES, JOIN_REQUEST_STATUSES, PERMISSION_KEYS, PLUGIN_STATUSES, PLUGIN_CATEGORIES, PLUGIN_CAPABILITIES, PLUGIN_UI_SLOT_TYPES, PLUGIN_RESERVED_COMPANY_ROUTE_SEGMENTS, PLUGIN_LAUNCHER_PLACEMENT_ZONES, PLUGIN_LAUNCHER_ACTIONS, PLUGIN_LAUNCHER_BOUNDS, PLUGIN_LAUNCHER_RENDER_ENVIRONMENTS, PLUGIN_UI_SLOT_ENTITY_TYPES, PLUGIN_STATE_SCOPE_KINDS;
14
14
  var init_constants = __esm({
15
15
  "../packages/shared/src/constants.ts"() {
16
16
  "use strict";
@@ -95,6 +95,15 @@ var init_constants = __esm({
95
95
  "pentagon",
96
96
  "fingerprint"
97
97
  ];
98
+ AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX = "dicebear:notionists:";
99
+ AGENT_AVATAR_BACKGROUND_PRESET_IDS = [
100
+ "mist",
101
+ "slate",
102
+ "sky",
103
+ "mint",
104
+ "peach",
105
+ "violet"
106
+ ];
98
107
  ISSUE_STATUSES = [
99
108
  "backlog",
100
109
  "todo",
@@ -632,9 +641,35 @@ var init_chat = __esm({
632
641
  question: z5.string().trim().min(1).max(240),
633
642
  options: z5.array(chatAskUserOptionSchema).min(2).max(3),
634
643
  allowFreeform: z5.boolean().optional()
644
+ }).superRefine((question, ctx) => {
645
+ const optionIds = /* @__PURE__ */ new Set();
646
+ question.options.forEach((option, index) => {
647
+ if (optionIds.has(option.id)) {
648
+ ctx.addIssue({
649
+ code: z5.ZodIssueCode.custom,
650
+ message: "Option ids must be unique within each question",
651
+ path: ["options", index, "id"]
652
+ });
653
+ return;
654
+ }
655
+ optionIds.add(option.id);
656
+ });
635
657
  });
636
658
  chatAskUserRequestSchema = z5.object({
637
659
  questions: z5.array(chatAskUserQuestionSchema).min(1).max(3)
660
+ }).superRefine((request, ctx) => {
661
+ const questionIds = /* @__PURE__ */ new Set();
662
+ request.questions.forEach((question, index) => {
663
+ if (questionIds.has(question.id)) {
664
+ ctx.addIssue({
665
+ code: z5.ZodIssueCode.custom,
666
+ message: "Question ids must be unique within requestUserInput",
667
+ path: ["questions", index, "id"]
668
+ });
669
+ return;
670
+ }
671
+ questionIds.add(question.id);
672
+ });
638
673
  });
639
674
  chatIssueRichReferenceSchema = z5.object({
640
675
  type: z5.literal("issue"),
@@ -1267,7 +1302,7 @@ var init_model_fallbacks = __esm({
1267
1302
 
1268
1303
  // ../packages/shared/src/validators/agent.ts
1269
1304
  import { z as z11 } from "zod";
1270
- var agentPermissionsSchema, agentInstructionsBundleModeSchema, updateAgentInstructionsBundleSchema, upsertAgentInstructionsFileSchema, agentRuntimeConfigSchema, optionalAgentNameSchema, uploadedAgentIconSchema, customAgentIconSchema, agentIconSchema, createAgentSchema, createAgentHireSchema, updateAgentSchema, updateAgentInstructionsPathSchema, createAgentKeySchema, wakeAgentSchema, resetAgentSessionSchema, testAgentRuntimeEnvironmentSchema, updateAgentPermissionsSchema;
1305
+ var agentPermissionsSchema, agentInstructionsBundleModeSchema, updateAgentInstructionsBundleSchema, upsertAgentInstructionsFileSchema, agentRuntimeConfigSchema, optionalAgentNameSchema, uploadedAgentIconSchema, diceBearNotionistsAgentIconSchema, agentIconSchema, createAgentSchema, createAgentHireSchema, updateAgentSchema, updateAgentInstructionsPathSchema, createAgentKeySchema, wakeAgentSchema, resetAgentSessionSchema, testAgentRuntimeEnvironmentSchema, updateAgentPermissionsSchema;
1271
1306
  var init_agent = __esm({
1272
1307
  "../packages/shared/src/validators/agent.ts"() {
1273
1308
  "use strict";
@@ -1312,10 +1347,19 @@ var init_agent = __esm({
1312
1347
  z11.string().trim().min(1).optional()
1313
1348
  );
1314
1349
  uploadedAgentIconSchema = z11.string().regex(
1315
- /^asset:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i,
1350
+ new RegExp(
1351
+ `^asset:[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}(?:\\?bg=(?:${AGENT_AVATAR_BACKGROUND_PRESET_IDS.join("|")}))?$`,
1352
+ "i"
1353
+ ),
1316
1354
  "Invalid uploaded avatar reference"
1317
1355
  );
1318
- customAgentIconSchema = z11.string().trim().min(1).max(24).refine((value) => !value.toLowerCase().startsWith("asset:"), "Invalid uploaded avatar reference").refine((value) => !/[<>\u0000-\u001f\u007f]/u.test(value), "Icon cannot contain markup or control characters");
1356
+ diceBearNotionistsAgentIconSchema = z11.string().regex(
1357
+ new RegExp(
1358
+ `^${AGENT_DICEBEAR_NOTIONISTS_ICON_PREFIX}[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}(?:\\?bg=(?:${AGENT_AVATAR_BACKGROUND_PRESET_IDS.join("|")}))?$`,
1359
+ "i"
1360
+ ),
1361
+ "Invalid DiceBear avatar reference"
1362
+ );
1319
1363
  agentIconSchema = z11.preprocess(
1320
1364
  (value) => {
1321
1365
  if (typeof value !== "string") return value;
@@ -1325,7 +1369,7 @@ var init_agent = __esm({
1325
1369
  z11.union([
1326
1370
  z11.enum(AGENT_ICON_NAMES),
1327
1371
  uploadedAgentIconSchema,
1328
- customAgentIconSchema
1372
+ diceBearNotionistsAgentIconSchema
1329
1373
  ]).nullable()
1330
1374
  );
1331
1375
  createAgentSchema = z11.object({
@@ -3716,7 +3760,7 @@ var init_server = __esm({
3716
3760
 
3717
3761
  // src/runtime/install.ts
3718
3762
  import { spawnSync } from "node:child_process";
3719
- import { mkdir, readFile, writeFile } from "node:fs/promises";
3763
+ import { mkdir, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
3720
3764
  import path6 from "node:path";
3721
3765
  import { createRequire } from "node:module";
3722
3766
  import { pathToFileURL } from "node:url";
@@ -3745,6 +3789,21 @@ async function readRuntimeInstallMetadata(cacheDir) {
3745
3789
  return null;
3746
3790
  }
3747
3791
  }
3792
+ async function writeRuntimeInstallMetadata(cacheDir, metadata) {
3793
+ await writeFile(path6.join(cacheDir, RUNTIME_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}
3794
+ `, "utf8");
3795
+ }
3796
+ async function touchRuntimeInstallMetadata(cacheDir) {
3797
+ try {
3798
+ const metadata = await readRuntimeInstallMetadata(cacheDir);
3799
+ if (!metadata) return;
3800
+ await writeRuntimeInstallMetadata(cacheDir, {
3801
+ ...metadata,
3802
+ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
3803
+ });
3804
+ } catch {
3805
+ }
3806
+ }
3748
3807
  async function isRuntimeCacheHit(options) {
3749
3808
  const packageName = options.packageName ?? RUNTIME_NPM_PACKAGE_NAME;
3750
3809
  const packageVersion = resolveRuntimePackageVersion(options.version);
@@ -3767,7 +3826,14 @@ async function ensureRuntimeInstalled(options) {
3767
3826
  const packageSpec = resolveRuntimePackageSpec(packageVersion, packageName);
3768
3827
  const command = `npm install --prefix ${cacheDir} --omit=dev --no-audit --no-fund ${packageSpec}`;
3769
3828
  if (await isRuntimeCacheHit({ cacheDir, version: packageVersion, packageName })) {
3770
- return { status: "hit", cacheDir, packageSpec, command, output: "" };
3829
+ await touchRuntimeInstallMetadata(cacheDir);
3830
+ const prune2 = await maybePruneRuntimeCache({
3831
+ homeDir: options.homeDir,
3832
+ requestedVersion: packageVersion,
3833
+ enabled: options.pruneRuntimeCache !== false,
3834
+ retention: options.retention
3835
+ });
3836
+ return { status: "hit", cacheDir, packageSpec, command, output: "", ...prune2 ? { prune: prune2 } : {} };
3771
3837
  }
3772
3838
  await mkdir(cacheDir, { recursive: true });
3773
3839
  await writeFile(path6.join(cacheDir, "package.json"), `${JSON.stringify({ private: true, type: "module" }, null, 2)}
@@ -3785,11 +3851,17 @@ async function ensureRuntimeInstalled(options) {
3785
3851
  version: 1,
3786
3852
  packageName,
3787
3853
  packageVersion,
3788
- installedAt: (/* @__PURE__ */ new Date()).toISOString()
3854
+ installedAt: (/* @__PURE__ */ new Date()).toISOString(),
3855
+ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString()
3789
3856
  };
3790
- await writeFile(path6.join(cacheDir, RUNTIME_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}
3791
- `, "utf8");
3792
- return { status: "installed", cacheDir, packageSpec, command, output };
3857
+ await writeRuntimeInstallMetadata(cacheDir, metadata);
3858
+ const prune = await maybePruneRuntimeCache({
3859
+ homeDir: options.homeDir,
3860
+ requestedVersion: packageVersion,
3861
+ enabled: options.pruneRuntimeCache !== false,
3862
+ retention: options.retention
3863
+ });
3864
+ return { status: "installed", cacheDir, packageSpec, command, output, ...prune ? { prune } : {} };
3793
3865
  }
3794
3866
  function resolveRuntimeServerEntrypoint(cacheDir, packageName = RUNTIME_NPM_PACKAGE_NAME) {
3795
3867
  return createRequire(path6.join(cacheDir, "package.json")).resolve(packageName);
@@ -3812,13 +3884,238 @@ function runNpmRuntimeInstall(spawnSyncImpl, cacheDir, packageSpec) {
3812
3884
  function collectSpawnOutput(result) {
3813
3885
  return [result.stdout, result.stderr, result.error instanceof Error ? result.error.message : null].filter((value) => typeof value === "string" && value.trim().length > 0).join("\n").trim();
3814
3886
  }
3815
- var RUNTIME_NPM_PACKAGE_NAME, RUNTIME_METADATA_FILE, RuntimeInstallError;
3887
+ async function maybePruneRuntimeCache(options) {
3888
+ if (!options.enabled) return null;
3889
+ return pruneRuntimeCache({
3890
+ ...options.retention,
3891
+ homeDir: options.homeDir,
3892
+ requestedVersion: options.retention?.requestedVersion ?? options.requestedVersion
3893
+ });
3894
+ }
3895
+ async function pruneRuntimeCache(options = {}) {
3896
+ const homeDir = options.homeDir ?? resolveRudderHomeDir();
3897
+ const now = options.now ?? /* @__PURE__ */ new Date();
3898
+ const entries = await scanRuntimeCacheEntries(homeDir);
3899
+ const activeVersions = await readActiveRuntimeVersions(homeDir);
3900
+ const protectedVersions = resolveProtectedRuntimeVersions(entries, {
3901
+ requestedVersion: options.requestedVersion,
3902
+ protectedVersions: [...options.protectedVersions ?? [], ...activeVersions],
3903
+ keepPreviousEntries: options.keepPreviousEntries ?? DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS
3904
+ });
3905
+ const protectedSet = new Set(protectedVersions);
3906
+ const maxEntries = options.maxEntries ?? DEFAULT_RUNTIME_CACHE_MAX_ENTRIES;
3907
+ const maxAgeMs = options.maxAgeMs ?? DEFAULT_RUNTIME_CACHE_MAX_AGE_MS;
3908
+ const maxTotalBytes = options.maxTotalBytes ?? DEFAULT_RUNTIME_CACHE_MAX_BYTES;
3909
+ const deletions = planRuntimeCacheDeletions(entries, {
3910
+ nowMs: now.getTime(),
3911
+ protectedVersions: protectedSet,
3912
+ maxEntries,
3913
+ maxAgeMs,
3914
+ maxTotalBytes
3915
+ });
3916
+ const deleted = [];
3917
+ const warnings = [];
3918
+ for (const entry of deletions) {
3919
+ try {
3920
+ await rm(entry.cacheDir, { recursive: true, force: true });
3921
+ deleted.push({
3922
+ cacheDir: entry.cacheDir,
3923
+ packageVersion: entry.packageVersion,
3924
+ sizeBytes: entry.sizeBytes
3925
+ });
3926
+ } catch (error) {
3927
+ warnings.push(
3928
+ `Failed to remove runtime cache ${entry.cacheDir}: ${error instanceof Error ? error.message : String(error)}`
3929
+ );
3930
+ }
3931
+ }
3932
+ return {
3933
+ scanned: entries.length,
3934
+ deleted,
3935
+ protectedVersions,
3936
+ freedBytes: deleted.reduce((total, entry) => total + entry.sizeBytes, 0),
3937
+ warnings
3938
+ };
3939
+ }
3940
+ async function scanRuntimeCacheEntries(homeDir) {
3941
+ const runtimesDir = path6.join(homeDir, "runtimes");
3942
+ const dirents = await readdir(runtimesDir, { withFileTypes: true }).catch(() => null);
3943
+ if (!dirents) return [];
3944
+ const entries = [];
3945
+ for (const dirent of dirents) {
3946
+ if (!dirent.isDirectory()) continue;
3947
+ const cacheDir = path6.join(runtimesDir, dirent.name);
3948
+ const metadata = await readRuntimeInstallMetadata(cacheDir);
3949
+ if (!metadata) continue;
3950
+ const fallbackStat = await safeStat(cacheDir);
3951
+ const installedAtMs = parseTimestampMs(metadata.installedAt) ?? Number(fallbackStat?.mtimeMs ?? 0);
3952
+ const lastUsedAtMs = parseTimestampMs(metadata.lastUsedAt) ?? installedAtMs;
3953
+ entries.push({
3954
+ cacheDir,
3955
+ packageVersion: metadata.packageVersion,
3956
+ installedAtMs,
3957
+ lastUsedAtMs,
3958
+ sizeBytes: await directorySizeBytes(cacheDir)
3959
+ });
3960
+ }
3961
+ return entries;
3962
+ }
3963
+ function parseTimestampMs(value) {
3964
+ if (!value) return null;
3965
+ const ms = Date.parse(value);
3966
+ return Number.isFinite(ms) ? ms : null;
3967
+ }
3968
+ async function safeStat(targetPath) {
3969
+ try {
3970
+ return await stat(targetPath);
3971
+ } catch {
3972
+ return null;
3973
+ }
3974
+ }
3975
+ async function directorySizeBytes(targetPath) {
3976
+ const dirents = await readdir(targetPath, { withFileTypes: true }).catch(() => null);
3977
+ if (!dirents) return 0;
3978
+ let total = 0;
3979
+ for (const dirent of dirents) {
3980
+ const entryPath = path6.join(targetPath, dirent.name);
3981
+ if (dirent.isSymbolicLink()) continue;
3982
+ if (dirent.isDirectory()) {
3983
+ total += await directorySizeBytes(entryPath);
3984
+ continue;
3985
+ }
3986
+ const entryStat = await safeStat(entryPath);
3987
+ total += Number(entryStat?.size ?? 0);
3988
+ }
3989
+ return total;
3990
+ }
3991
+ async function readActiveRuntimeVersions(homeDir) {
3992
+ const instancesDir = path6.join(homeDir, "instances");
3993
+ const dirents = await readdir(instancesDir, { withFileTypes: true }).catch(() => null);
3994
+ if (!dirents) return [];
3995
+ const versions = /* @__PURE__ */ new Set();
3996
+ for (const dirent of dirents) {
3997
+ if (!dirent.isDirectory()) continue;
3998
+ try {
3999
+ const descriptorPath = path6.join(instancesDir, dirent.name, "runtime", "server.json");
4000
+ const parsed = JSON.parse(await readFile(descriptorPath, "utf8"));
4001
+ if (typeof parsed.version !== "string") continue;
4002
+ if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0 && isPidRunning(parsed.pid)) {
4003
+ versions.add(parsed.version);
4004
+ }
4005
+ } catch {
4006
+ continue;
4007
+ }
4008
+ }
4009
+ return [...versions];
4010
+ }
4011
+ function isPidRunning(pid) {
4012
+ try {
4013
+ process.kill(pid, 0);
4014
+ return true;
4015
+ } catch {
4016
+ return false;
4017
+ }
4018
+ }
4019
+ function resolveProtectedRuntimeVersions(entries, options) {
4020
+ const protectedVersions = /* @__PURE__ */ new Set();
4021
+ const requestedVersion = options.requestedVersion ? resolveRuntimePackageVersion(options.requestedVersion) : null;
4022
+ if (requestedVersion) protectedVersions.add(requestedVersion);
4023
+ for (const version of options.protectedVersions) {
4024
+ const normalized = version.trim();
4025
+ if (normalized) protectedVersions.add(normalized);
4026
+ }
4027
+ const latestStable = latestRuntimeVersion(entries.filter((entry) => isStableVersion(entry.packageVersion)));
4028
+ if (latestStable) protectedVersions.add(latestStable);
4029
+ const latestCanary = latestRuntimeVersion(entries.filter((entry) => isCanaryVersion(entry.packageVersion)));
4030
+ if (latestCanary) protectedVersions.add(latestCanary);
4031
+ const previousEntries = [...entries].filter((entry) => entry.packageVersion !== requestedVersion).sort((a, b) => b.lastUsedAtMs - a.lastUsedAtMs);
4032
+ for (const entry of previousEntries.slice(0, Math.max(0, options.keepPreviousEntries))) {
4033
+ protectedVersions.add(entry.packageVersion);
4034
+ }
4035
+ return [...protectedVersions].sort();
4036
+ }
4037
+ function planRuntimeCacheDeletions(entries, options) {
4038
+ const deletions = /* @__PURE__ */ new Set();
4039
+ const oldestFirst = [...entries].sort((a, b) => a.lastUsedAtMs - b.lastUsedAtMs);
4040
+ const canDelete = (entry) => !options.protectedVersions.has(entry.packageVersion) && !deletions.has(entry.cacheDir);
4041
+ const mark = (entry) => {
4042
+ if (canDelete(entry)) deletions.add(entry.cacheDir);
4043
+ };
4044
+ if (options.maxAgeMs >= 0) {
4045
+ for (const entry of oldestFirst) {
4046
+ if (options.nowMs - entry.lastUsedAtMs > options.maxAgeMs) mark(entry);
4047
+ }
4048
+ }
4049
+ if (options.maxEntries > 0) {
4050
+ for (const entry of oldestFirst) {
4051
+ if (entries.length - deletions.size <= options.maxEntries) break;
4052
+ mark(entry);
4053
+ }
4054
+ }
4055
+ if (options.maxTotalBytes > 0) {
4056
+ let remainingBytes = entries.reduce((total, entry) => total + entry.sizeBytes, 0) - [...deletions].reduce((total, cacheDir) => total + (entries.find((entry) => entry.cacheDir === cacheDir)?.sizeBytes ?? 0), 0);
4057
+ for (const entry of oldestFirst) {
4058
+ if (remainingBytes <= options.maxTotalBytes) break;
4059
+ if (!canDelete(entry)) continue;
4060
+ deletions.add(entry.cacheDir);
4061
+ remainingBytes -= entry.sizeBytes;
4062
+ }
4063
+ }
4064
+ return entries.filter((entry) => deletions.has(entry.cacheDir));
4065
+ }
4066
+ function latestRuntimeVersion(entries) {
4067
+ let latest = null;
4068
+ for (const entry of entries) {
4069
+ if (!latest || compareRuntimeVersions(entry.packageVersion, latest) > 0) {
4070
+ latest = entry.packageVersion;
4071
+ }
4072
+ }
4073
+ return latest;
4074
+ }
4075
+ function isStableVersion(version) {
4076
+ return /^\d+\.\d+\.\d+$/.test(version);
4077
+ }
4078
+ function isCanaryVersion(version) {
4079
+ return /^\d+\.\d+\.\d+-canary\.\d+$/.test(version);
4080
+ }
4081
+ function compareRuntimeVersions(a, b) {
4082
+ const parsedA = parseRuntimeVersion(a);
4083
+ const parsedB = parseRuntimeVersion(b);
4084
+ if (!parsedA || !parsedB) return a.localeCompare(b);
4085
+ for (const key of ["major", "minor", "patch"]) {
4086
+ if (parsedA[key] !== parsedB[key]) return parsedA[key] - parsedB[key];
4087
+ }
4088
+ if (parsedA.prerelease === null && parsedB.prerelease !== null) return 1;
4089
+ if (parsedA.prerelease !== null && parsedB.prerelease === null) return -1;
4090
+ if (parsedA.canaryNumber !== null && parsedB.canaryNumber !== null) {
4091
+ return parsedA.canaryNumber - parsedB.canaryNumber;
4092
+ }
4093
+ return (parsedA.prerelease ?? "").localeCompare(parsedB.prerelease ?? "");
4094
+ }
4095
+ function parseRuntimeVersion(version) {
4096
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(version);
4097
+ if (!match) return null;
4098
+ const prerelease = match[4] ?? null;
4099
+ const canaryMatch = prerelease ? /^canary\.(\d+)$/.exec(prerelease) : null;
4100
+ return {
4101
+ major: Number(match[1]),
4102
+ minor: Number(match[2]),
4103
+ patch: Number(match[3]),
4104
+ prerelease,
4105
+ canaryNumber: canaryMatch ? Number(canaryMatch[1]) : null
4106
+ };
4107
+ }
4108
+ var RUNTIME_NPM_PACKAGE_NAME, RUNTIME_METADATA_FILE, DEFAULT_RUNTIME_CACHE_MAX_ENTRIES, DEFAULT_RUNTIME_CACHE_MAX_AGE_MS, DEFAULT_RUNTIME_CACHE_MAX_BYTES, DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS, RuntimeInstallError;
3816
4109
  var init_install = __esm({
3817
4110
  "src/runtime/install.ts"() {
3818
4111
  "use strict";
3819
4112
  init_home();
3820
4113
  RUNTIME_NPM_PACKAGE_NAME = "@rudderhq/server";
3821
4114
  RUNTIME_METADATA_FILE = "runtime.json";
4115
+ DEFAULT_RUNTIME_CACHE_MAX_ENTRIES = 5;
4116
+ DEFAULT_RUNTIME_CACHE_MAX_AGE_MS = 14 * 24 * 60 * 60 * 1e3;
4117
+ DEFAULT_RUNTIME_CACHE_MAX_BYTES = 2 * 1024 * 1024 * 1024;
4118
+ DEFAULT_RUNTIME_CACHE_KEEP_PREVIOUS = 1;
3822
4119
  RuntimeInstallError = class extends Error {
3823
4120
  cacheDir;
3824
4121
  command;
@@ -6047,11 +6344,12 @@ ${err instanceof Error ? err.message : String(err)}`
6047
6344
 
6048
6345
  // src/commands/start.ts
6049
6346
  init_install2();
6347
+ init_home();
6050
6348
  init_install();
6051
6349
  import { spawn, spawnSync as spawnSync3 } from "node:child_process";
6052
6350
  import { createHash } from "node:crypto";
6053
6351
  import { constants as fsConstants, createWriteStream, mkdirSync, readFileSync as readFileSync2 } from "node:fs";
6054
- import { access, chmod, copyFile, cp, mkdtemp, mkdir as mkdir2, readFile as readFile2, readdir, rm, writeFile as writeFile2 } from "node:fs/promises";
6352
+ import { access, chmod, copyFile, cp, mkdtemp, mkdir as mkdir2, readFile as readFile2, readdir as readdir2, rm as rm2, writeFile as writeFile2 } from "node:fs/promises";
6055
6353
  import { homedir, tmpdir } from "node:os";
6056
6354
  import path11 from "node:path";
6057
6355
  import { Readable, Transform } from "node:stream";
@@ -6177,6 +6475,7 @@ var CLI_REGISTRY_LATEST_URL = "https://registry.npmjs.org/@rudderhq%2fcli/latest
6177
6475
  var DESKTOP_APP_NAME = "Rudder";
6178
6476
  var DESKTOP_METADATA_FILE = ".rudder-desktop-install.json";
6179
6477
  var DESKTOP_CHECKSUM_ASSET_NAME = "SHASUMS256.txt";
6478
+ var DESKTOP_ASSET_CACHE_DIR = "desktop-assets";
6180
6479
  var GITHUB_ASSET_DOWNLOAD_ACCEPT = "application/octet-stream";
6181
6480
  function normalizeProgressTotal(totalBytes) {
6182
6481
  return typeof totalBytes === "number" && Number.isFinite(totalBytes) && totalBytes > 0 ? totalBytes : null;
@@ -6558,6 +6857,44 @@ async function downloadChecksums(checksumAsset, outputDir, progressFactory = cre
6558
6857
  const checksumPath = await downloadAsset(checksumAsset, outputDir, progressFactory);
6559
6858
  return parseChecksumFile(readFileSync2(checksumPath, "utf8"));
6560
6859
  }
6860
+ function normalizeDesktopAssetChecksum(checksum) {
6861
+ const normalized = checksum.trim().toLowerCase();
6862
+ if (!/^[a-f0-9]{64}$/.test(normalized)) {
6863
+ throw new Error("Desktop asset cache requires a SHA-256 checksum.");
6864
+ }
6865
+ return normalized;
6866
+ }
6867
+ function resolveDesktopAssetCacheDir(assetChecksum, homeDir = resolveRudderHomeDir()) {
6868
+ return path11.join(homeDir, DESKTOP_ASSET_CACHE_DIR, normalizeDesktopAssetChecksum(assetChecksum));
6869
+ }
6870
+ function resolveDesktopCachedAssetPath(assetName, assetChecksum, homeDir = resolveRudderHomeDir()) {
6871
+ return path11.join(resolveDesktopAssetCacheDir(assetChecksum, homeDir), path11.basename(assetName));
6872
+ }
6873
+ async function downloadDesktopAssetWithCache(asset, expectedChecksum, options = {}) {
6874
+ const normalizedChecksum = normalizeDesktopAssetChecksum(expectedChecksum);
6875
+ const cachePath = resolveDesktopCachedAssetPath(asset.name, normalizedChecksum, options.homeDir);
6876
+ if (await pathExists(cachePath)) {
6877
+ try {
6878
+ const checksum = assertChecksumMatch(cachePath, normalizedChecksum);
6879
+ return { path: cachePath, checksum, cacheStatus: "hit" };
6880
+ } catch {
6881
+ await rm2(cachePath, { force: true });
6882
+ }
6883
+ }
6884
+ const outputDir = options.outputDir ?? await mkdtemp(path11.join(tmpdir(), "rudder-desktop-installer."));
6885
+ const removeOutputDir = options.outputDir ? false : true;
6886
+ try {
6887
+ const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory);
6888
+ const checksum = assertChecksumMatch(downloadedPath, normalizedChecksum);
6889
+ await mkdir2(path11.dirname(cachePath), { recursive: true });
6890
+ if (path11.resolve(downloadedPath) !== path11.resolve(cachePath)) {
6891
+ await copyFile(downloadedPath, cachePath);
6892
+ }
6893
+ return { path: cachePath, checksum, cacheStatus: "miss" };
6894
+ } finally {
6895
+ if (removeOutputDir) await rm2(outputDir, { recursive: true, force: true });
6896
+ }
6897
+ }
6561
6898
  async function pathExists(targetPath) {
6562
6899
  try {
6563
6900
  await access(targetPath, fsConstants.F_OK);
@@ -6596,7 +6933,7 @@ function isSuccessfulRobocopyExitCode(status) {
6596
6933
  return typeof status === "number" && status >= 0 && status <= 7;
6597
6934
  }
6598
6935
  async function extractZip(zipPath, outputDir, target) {
6599
- await rm(outputDir, { recursive: true, force: true });
6936
+ await rm2(outputDir, { recursive: true, force: true });
6600
6937
  await mkdir2(outputDir, { recursive: true });
6601
6938
  if (target.platform === "macos") {
6602
6939
  runChecked("ditto", ["-x", "-k", zipPath, outputDir]);
@@ -6611,7 +6948,7 @@ async function extractZip(zipPath, outputDir, target) {
6611
6948
  }
6612
6949
  async function findPath(root, predicate, maxDepth = 5) {
6613
6950
  async function visit(dir, depth) {
6614
- const entries = await readdir(dir, { withFileTypes: true });
6951
+ const entries = await readdir2(dir, { withFileTypes: true });
6615
6952
  for (const entry of entries) {
6616
6953
  const fullPath = path11.join(dir, entry.name);
6617
6954
  if (predicate(fullPath, entry.isDirectory())) return fullPath;
@@ -6686,13 +7023,13 @@ async function requestDesktopQuit(executablePath, target) {
6686
7023
  try {
6687
7024
  return await waitForUpdateQuitResponse(responsePath);
6688
7025
  } finally {
6689
- await rm(responsePath, { force: true });
7026
+ await rm2(responsePath, { force: true });
6690
7027
  }
6691
7028
  }
6692
7029
  async function removePathWithRetry(targetPath, attempts = 5) {
6693
7030
  for (let attempt = 0; attempt < attempts; attempt += 1) {
6694
7031
  try {
6695
- await rm(targetPath, { recursive: true, force: true });
7032
+ await rm2(targetPath, { recursive: true, force: true });
6696
7033
  if (!await pathExists(targetPath)) return true;
6697
7034
  } catch {
6698
7035
  }
@@ -6746,7 +7083,7 @@ async function installPortableDesktop(installerPath, paths, target) {
6746
7083
  await mkdir2(path11.dirname(paths.installRoot), { recursive: true });
6747
7084
  await copyPortableAppBundle(appSource, paths.installRoot);
6748
7085
  } finally {
6749
- await rm(extractDir, { recursive: true, force: true });
7086
+ await rm2(extractDir, { recursive: true, force: true });
6750
7087
  }
6751
7088
  }
6752
7089
  async function copyPortableAppBundle(sourcePath, destinationPath) {
@@ -6997,11 +7334,24 @@ async function startCommand(opts) {
6997
7334
  desktopProgressJson ? "preparing_restart" : null
6998
7335
  );
6999
7336
  } else {
7000
- const installerPath = await downloadAsset(asset, outputDir, progressFactory);
7337
+ const cachedAsset = await downloadDesktopAssetWithCache(asset, expectedChecksum, {
7338
+ outputDir,
7339
+ progressFactory
7340
+ });
7341
+ if (cachedAsset.cacheStatus === "hit") {
7342
+ p13.log.success(`Desktop asset cache hit at ${pc8.cyan(cachedAsset.path)}.`);
7343
+ if (desktopProgressJson) {
7344
+ writeDesktopProgress({
7345
+ phase: "downloading_asset",
7346
+ message: `Desktop asset cache hit for ${asset.name}.`,
7347
+ percent: 100
7348
+ });
7349
+ }
7350
+ }
7001
7351
  const checksum = await runStartPhase(
7002
7352
  "Verifying Desktop checksum...",
7003
- `Verified ${pc8.cyan(path11.basename(installerPath))}.`,
7004
- () => assertChecksumMatch(installerPath, expectedChecksum),
7353
+ `Verified ${pc8.cyan(path11.basename(cachedAsset.path))}.`,
7354
+ () => assertChecksumMatch(cachedAsset.path, expectedChecksum),
7005
7355
  desktopProgressJson ? "verifying_checksum" : null
7006
7356
  );
7007
7357
  if (desktopProgressJson && opts.desktopWaitForApply === true) {
@@ -7025,7 +7375,7 @@ async function startCommand(opts) {
7025
7375
  await runStartPhase(
7026
7376
  "Installing portable Desktop app...",
7027
7377
  `Installed Rudder Desktop to ${pc8.cyan(installPaths.appPath)}.`,
7028
- () => installPortableDesktop(installerPath, installPaths, target),
7378
+ () => installPortableDesktop(cachedAsset.path, installPaths, target),
7029
7379
  desktopProgressJson ? "preparing_restart" : null
7030
7380
  );
7031
7381
  await runStartPhase(
@@ -8144,7 +8494,7 @@ function registerContextCommands(program) {
8144
8494
  }
8145
8495
 
8146
8496
  // src/commands/client/company.ts
8147
- import { mkdir as mkdir3, readdir as readdir2, readFile as readFile3, stat, writeFile as writeFile3 } from "node:fs/promises";
8497
+ import { mkdir as mkdir3, readdir as readdir3, readFile as readFile3, stat as stat2, writeFile as writeFile3 } from "node:fs/promises";
8148
8498
  import path15 from "node:path";
8149
8499
  import * as p15 from "@clack/prompts";
8150
8500
  import pc14 from "picocolors";
@@ -8879,14 +9229,14 @@ function normalizeGithubImportSource(input, refOverride) {
8879
9229
  }
8880
9230
  async function pathExists2(inputPath) {
8881
9231
  try {
8882
- await stat(path15.resolve(inputPath));
9232
+ await stat2(path15.resolve(inputPath));
8883
9233
  return true;
8884
9234
  } catch {
8885
9235
  return false;
8886
9236
  }
8887
9237
  }
8888
9238
  async function collectPackageFiles(root, current, files) {
8889
- const entries = await readdir2(current, { withFileTypes: true });
9239
+ const entries = await readdir3(current, { withFileTypes: true });
8890
9240
  for (const entry of entries) {
8891
9241
  if (entry.name.startsWith(".git")) continue;
8892
9242
  const absolutePath = path15.join(current, entry.name);
@@ -8902,7 +9252,7 @@ async function collectPackageFiles(root, current, files) {
8902
9252
  }
8903
9253
  async function resolveInlineSourceFromPath(inputPath) {
8904
9254
  const resolved = path15.resolve(inputPath);
8905
- const resolvedStat = await stat(resolved);
9255
+ const resolvedStat = await stat2(resolved);
8906
9256
  if (resolvedStat.isFile() && path15.extname(resolved).toLowerCase() === ".zip") {
8907
9257
  const archive = await readZipArchive(await readFile3(resolved));
8908
9258
  const filteredFiles = Object.fromEntries(
@@ -8938,12 +9288,12 @@ async function writeExportToFolder(outDir, exported) {
8938
9288
  }
8939
9289
  async function confirmOverwriteExportDirectory(outDir) {
8940
9290
  const root = path15.resolve(outDir);
8941
- const stats = await stat(root).catch(() => null);
9291
+ const stats = await stat2(root).catch(() => null);
8942
9292
  if (!stats) return;
8943
9293
  if (!stats.isDirectory()) {
8944
9294
  throw new Error(`Export output path ${root} exists and is not a directory.`);
8945
9295
  }
8946
- const entries = await readdir2(root);
9296
+ const entries = await readdir3(root);
8947
9297
  if (entries.length === 0) return;
8948
9298
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
8949
9299
  throw new Error(`Export output directory ${root} already contains files. Re-run interactively or choose an empty directory.`);
@@ -9365,7 +9715,7 @@ ${organizationUrl}`);
9365
9715
 
9366
9716
  // src/commands/client/issue.ts
9367
9717
  init_src();
9368
- import { readFile as readFile4, stat as stat2 } from "node:fs/promises";
9718
+ import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
9369
9719
  import path16 from "node:path";
9370
9720
 
9371
9721
  // src/agent-v1-registry.ts
@@ -10260,7 +10610,7 @@ ${imageBlock}` : imageBlock;
10260
10610
  }
10261
10611
  async function uploadIssueCommentImage(ctx, issue, imagePath) {
10262
10612
  const resolvedPath = path16.resolve(process.cwd(), imagePath);
10263
- const stats = await stat2(resolvedPath).catch((err) => {
10613
+ const stats = await stat3(resolvedPath).catch((err) => {
10264
10614
  throw new Error(`Unable to read image ${imagePath}: ${err instanceof Error ? err.message : String(err)}`);
10265
10615
  });
10266
10616
  if (!stats.isFile()) {