@rudderhq/cli 0.7.13 → 0.7.14
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 +849 -478
- package/dist/index.js.map +4 -4
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -7464,6 +7464,7 @@ import { execFile } from "node:child_process";
|
|
|
7464
7464
|
import { existsSync as existsSync3 } from "node:fs";
|
|
7465
7465
|
import fs6 from "node:fs/promises";
|
|
7466
7466
|
import path8 from "node:path";
|
|
7467
|
+
import { performance } from "node:perf_hooks";
|
|
7467
7468
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
7468
7469
|
import { promisify } from "node:util";
|
|
7469
7470
|
function nativeTarget() {
|
|
@@ -7496,14 +7497,14 @@ function parseEnvelope(stdout, capability) {
|
|
|
7496
7497
|
}
|
|
7497
7498
|
return envelope;
|
|
7498
7499
|
}
|
|
7499
|
-
async function runNativePayload(capability, args, commandMayAccept) {
|
|
7500
|
+
async function runNativePayload(capability, args, commandMayAccept, timeoutMs = TIMEOUT_MS) {
|
|
7500
7501
|
let stdout = "";
|
|
7501
7502
|
let stderr = "";
|
|
7502
7503
|
try {
|
|
7503
7504
|
const command = resolveNativeCommand(resolveNativePayloadBinary(), args);
|
|
7504
7505
|
const result = await execFileAsync(command.command, command.args, {
|
|
7505
7506
|
encoding: "utf8",
|
|
7506
|
-
timeout: TIMEOUT_MS,
|
|
7507
|
+
timeout: Math.max(1, Math.min(TIMEOUT_MS, timeoutMs)),
|
|
7507
7508
|
maxBuffer: OUTPUT_LIMIT_BYTES,
|
|
7508
7509
|
windowsHide: true
|
|
7509
7510
|
});
|
|
@@ -7522,6 +7523,9 @@ async function runNativePayload(capability, args, commandMayAccept) {
|
|
|
7522
7523
|
envelope2
|
|
7523
7524
|
);
|
|
7524
7525
|
}
|
|
7526
|
+
if (detail.code === "ETIMEDOUT" || detail.killed === true || detail.signal === "SIGTERM") {
|
|
7527
|
+
throw new NativePayloadError("deadline_exceeded", commandMayAccept, detail.code ?? detail.signal);
|
|
7528
|
+
}
|
|
7525
7529
|
const failedBeforeSpawn = detail.code === "ENOENT" || detail.code === "EACCES";
|
|
7526
7530
|
throw new NativePayloadError("process_failed", commandMayAccept && !failedBeforeSpawn, stderr || detail.code);
|
|
7527
7531
|
}
|
|
@@ -7544,16 +7548,16 @@ function nativePayloadPolicy() {
|
|
|
7544
7548
|
legacyToggleEnvs: ["RUDDER_NATIVE_RUNTIME_PAYLOAD"]
|
|
7545
7549
|
});
|
|
7546
7550
|
}
|
|
7547
|
-
async function verifyNativePayload(archivePath, expectedSha256, maxArchiveBytes) {
|
|
7551
|
+
async function verifyNativePayload(archivePath, expectedSha256, maxArchiveBytes, timeoutMs) {
|
|
7548
7552
|
return runNativePayload("payload.verify", [
|
|
7549
7553
|
"payload",
|
|
7550
7554
|
"verify",
|
|
7551
7555
|
path8.resolve(archivePath),
|
|
7552
7556
|
expectedSha256,
|
|
7553
7557
|
String(maxArchiveBytes)
|
|
7554
|
-
], false);
|
|
7558
|
+
], false, timeoutMs);
|
|
7555
7559
|
}
|
|
7556
|
-
async function extractNativePayload(archivePath, stagingPath, maxArchiveBytes) {
|
|
7560
|
+
async function extractNativePayload(archivePath, stagingPath, maxArchiveBytes, timeoutMs) {
|
|
7557
7561
|
try {
|
|
7558
7562
|
return await runNativePayload("payload.extract", [
|
|
7559
7563
|
"payload",
|
|
@@ -7565,7 +7569,7 @@ async function extractNativePayload(archivePath, stagingPath, maxArchiveBytes) {
|
|
|
7565
7569
|
String(maxArchiveBytes),
|
|
7566
7570
|
String(maxArchiveBytes * 2),
|
|
7567
7571
|
"0"
|
|
7568
|
-
], true);
|
|
7572
|
+
], true, timeoutMs);
|
|
7569
7573
|
} catch (error) {
|
|
7570
7574
|
if (error instanceof NativePayloadError && error.code === "process_failed" && !existsSync3(stagingPath)) {
|
|
7571
7575
|
throw new NativePayloadError("process_failed", false, error.message);
|
|
@@ -7573,24 +7577,53 @@ async function extractNativePayload(archivePath, stagingPath, maxArchiveBytes) {
|
|
|
7573
7577
|
throw error;
|
|
7574
7578
|
}
|
|
7575
7579
|
}
|
|
7576
|
-
async function probeNativePayloadVersion(rootPath, executable) {
|
|
7580
|
+
async function probeNativePayloadVersion(rootPath, executable, timeoutMs) {
|
|
7577
7581
|
return runNativePayload("payload.probeVersion", [
|
|
7578
7582
|
"payload",
|
|
7579
7583
|
"probe-version",
|
|
7580
7584
|
path8.resolve(rootPath),
|
|
7581
7585
|
executable,
|
|
7582
7586
|
"PostgreSQL 18.4"
|
|
7583
|
-
], true);
|
|
7587
|
+
], true, timeoutMs);
|
|
7584
7588
|
}
|
|
7585
|
-
async function publishNativePayload(stagingPath, destinationPath) {
|
|
7589
|
+
async function publishNativePayload(stagingPath, destinationPath, timeoutMs) {
|
|
7586
7590
|
return runNativePayload("payload.publish", [
|
|
7587
7591
|
"payload",
|
|
7588
7592
|
"publish",
|
|
7589
7593
|
path8.resolve(stagingPath),
|
|
7590
7594
|
path8.resolve(destinationPath)
|
|
7591
|
-
], true);
|
|
7595
|
+
], true, timeoutMs);
|
|
7592
7596
|
}
|
|
7593
7597
|
async function tryInstallNativePayload(input) {
|
|
7598
|
+
const now = input.now ?? (() => performance.now());
|
|
7599
|
+
const expiresAt = input.timeoutMs === void 0 ? null : now() + input.timeoutMs;
|
|
7600
|
+
const remainingTimeout = (accepted) => {
|
|
7601
|
+
if (expiresAt === null) return void 0;
|
|
7602
|
+
const remaining = Math.ceil(expiresAt - now());
|
|
7603
|
+
if (remaining <= 0) throw new NativePayloadError("deadline_exceeded", accepted);
|
|
7604
|
+
return remaining;
|
|
7605
|
+
};
|
|
7606
|
+
const runCallbackWithinDeadline = async (accepted, callback) => {
|
|
7607
|
+
const controller = new AbortController();
|
|
7608
|
+
const timeoutMs = remainingTimeout(accepted);
|
|
7609
|
+
let timer;
|
|
7610
|
+
const timeoutPromise = timeoutMs === void 0 ? null : new Promise((_resolve, reject) => {
|
|
7611
|
+
timer = setTimeout(() => {
|
|
7612
|
+
controller.abort();
|
|
7613
|
+
reject(new NativePayloadError("deadline_exceeded", accepted));
|
|
7614
|
+
}, timeoutMs);
|
|
7615
|
+
});
|
|
7616
|
+
const context = {
|
|
7617
|
+
signal: controller.signal,
|
|
7618
|
+
remainingMs: () => remainingTimeout(accepted)
|
|
7619
|
+
};
|
|
7620
|
+
try {
|
|
7621
|
+
const operation = callback(context);
|
|
7622
|
+
return timeoutPromise ? await Promise.race([operation, timeoutPromise]) : await operation;
|
|
7623
|
+
} finally {
|
|
7624
|
+
if (timer) clearTimeout(timer);
|
|
7625
|
+
}
|
|
7626
|
+
};
|
|
7594
7627
|
const policy = nativePayloadPolicy();
|
|
7595
7628
|
if (!policy.enabled) return {
|
|
7596
7629
|
installed: false,
|
|
@@ -7613,9 +7646,9 @@ async function tryInstallNativePayload(input) {
|
|
|
7613
7646
|
}
|
|
7614
7647
|
try {
|
|
7615
7648
|
if (expectedSha256) {
|
|
7616
|
-
await verifyNativePayload(input.archivePath, expectedSha256, input.maxArchiveBytes);
|
|
7649
|
+
await verifyNativePayload(input.archivePath, expectedSha256, input.maxArchiveBytes, remainingTimeout(false));
|
|
7617
7650
|
}
|
|
7618
|
-
await extractNativePayload(input.archivePath, input.extractPath, input.maxArchiveBytes);
|
|
7651
|
+
await extractNativePayload(input.archivePath, input.extractPath, input.maxArchiveBytes, remainingTimeout(false));
|
|
7619
7652
|
} catch (error) {
|
|
7620
7653
|
const fallbackSafe = error instanceof NativePayloadError && error.fallbackSafe;
|
|
7621
7654
|
if (!policy.fallbackAllowed || !fallbackSafe) throw error;
|
|
@@ -7634,10 +7667,16 @@ async function tryInstallNativePayload(input) {
|
|
|
7634
7667
|
};
|
|
7635
7668
|
}
|
|
7636
7669
|
try {
|
|
7637
|
-
const versionExecutable = await
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7670
|
+
const versionExecutable = await runCallbackWithinDeadline(
|
|
7671
|
+
true,
|
|
7672
|
+
(context) => input.preparePublish(input.extractPath, input.publishStagingPath, context)
|
|
7673
|
+
);
|
|
7674
|
+
await probeNativePayloadVersion(input.publishStagingPath, versionExecutable, remainingTimeout(true));
|
|
7675
|
+
const published = await publishNativePayload(input.publishStagingPath, input.destinationPath, remainingTimeout(true));
|
|
7676
|
+
await runCallbackWithinDeadline(
|
|
7677
|
+
true,
|
|
7678
|
+
(context) => input.validatePublished(input.destinationPath, context)
|
|
7679
|
+
);
|
|
7641
7680
|
return {
|
|
7642
7681
|
installed: true,
|
|
7643
7682
|
fallbackCode: null,
|
|
@@ -7651,7 +7690,9 @@ async function tryInstallNativePayload(input) {
|
|
|
7651
7690
|
}
|
|
7652
7691
|
};
|
|
7653
7692
|
} finally {
|
|
7654
|
-
|
|
7693
|
+
const cleanup = input.cleanupPublishStaging ?? ((publishStagingPath) => fs6.rm(publishStagingPath, { recursive: true, force: true }));
|
|
7694
|
+
void cleanup(input.publishStagingPath).catch(() => {
|
|
7695
|
+
});
|
|
7655
7696
|
}
|
|
7656
7697
|
}
|
|
7657
7698
|
var execFileAsync, PROTOCOL_VERSION, OUTPUT_LIMIT_BYTES, TIMEOUT_MS, NativePayloadError;
|
|
@@ -7689,81 +7730,26 @@ var init_native_payload = __esm({
|
|
|
7689
7730
|
}
|
|
7690
7731
|
});
|
|
7691
7732
|
|
|
7692
|
-
// src/runtime/postgres-payload.ts
|
|
7693
|
-
import { cp, mkdir, stat } from "node:fs/promises";
|
|
7694
|
-
import path9 from "node:path";
|
|
7695
|
-
async function copyRuntimePostgresPayload(sourceRuntimeDir, targetRuntimeDir, sourceShareDir = path9.join(sourceRuntimeDir, "share")) {
|
|
7696
|
-
await mkdir(targetRuntimeDir, { recursive: true });
|
|
7697
|
-
for (const directoryName of ["bin", "lib"]) {
|
|
7698
|
-
const sourceDirectory = path9.join(sourceRuntimeDir, directoryName);
|
|
7699
|
-
if (!await stat(sourceDirectory).catch(() => null)) continue;
|
|
7700
|
-
await cp(
|
|
7701
|
-
sourceDirectory,
|
|
7702
|
-
path9.join(targetRuntimeDir, directoryName),
|
|
7703
|
-
{ recursive: true, dereference: true }
|
|
7704
|
-
);
|
|
7705
|
-
}
|
|
7706
|
-
await cp(
|
|
7707
|
-
sourceShareDir,
|
|
7708
|
-
path9.join(targetRuntimeDir, "share"),
|
|
7709
|
-
{ recursive: true, dereference: true }
|
|
7710
|
-
);
|
|
7711
|
-
}
|
|
7712
|
-
var init_postgres_payload = __esm({
|
|
7713
|
-
"src/runtime/postgres-payload.ts"() {
|
|
7714
|
-
"use strict";
|
|
7715
|
-
}
|
|
7716
|
-
});
|
|
7717
|
-
|
|
7718
7733
|
// src/runtime/postgres-runtime-download.ts
|
|
7719
7734
|
import { createHash as createHash2 } from "node:crypto";
|
|
7720
7735
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
7721
|
-
import { copyFile, stat as stat2 } from "node:fs/promises";
|
|
7722
7736
|
import { Readable, Transform } from "node:stream";
|
|
7723
7737
|
import { pipeline } from "node:stream/promises";
|
|
7724
7738
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
7725
|
-
async function downloadRuntimePostgresArchive(url, targetPath, trustedSha256) {
|
|
7739
|
+
async function downloadRuntimePostgresArchive(url, targetPath, trustedSha256, options = {}) {
|
|
7726
7740
|
const expectedSha256 = (trustedSha256 ?? process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV])?.trim().toLowerCase() || null;
|
|
7727
7741
|
if (expectedSha256 && !/^[a-f0-9]{64}$/.test(expectedSha256)) {
|
|
7728
7742
|
throw new Error(`${RUDDER_POSTGRES_RUNTIME_ARCHIVE_SHA256_ENV} must be a 64-character SHA-256 digest`);
|
|
7729
7743
|
}
|
|
7730
7744
|
const configuredMaxBytes = Number.parseInt(process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV] ?? "", 10);
|
|
7731
7745
|
const maxBytes = Number.isSafeInteger(configuredMaxBytes) && configuredMaxBytes > 0 ? configuredMaxBytes : DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES;
|
|
7732
|
-
async function verifyFile(filePath) {
|
|
7733
|
-
if (!expectedSha256) return;
|
|
7734
|
-
const hash = createHash2("sha256");
|
|
7735
|
-
await new Promise((resolve, reject) => {
|
|
7736
|
-
const stream = createReadStream(filePath);
|
|
7737
|
-
stream.on("data", (chunk) => hash.update(chunk));
|
|
7738
|
-
stream.on("error", reject);
|
|
7739
|
-
stream.on("end", resolve);
|
|
7740
|
-
});
|
|
7741
|
-
const actual = hash.digest("hex");
|
|
7742
|
-
if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
|
|
7743
|
-
}
|
|
7744
|
-
if (url.startsWith("file://")) {
|
|
7745
|
-
await copyFile(fileURLToPath4(url), targetPath);
|
|
7746
|
-
const archiveStat = await stat2(targetPath);
|
|
7747
|
-
if (archiveStat.size > maxBytes) throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
|
|
7748
|
-
await verifyFile(targetPath);
|
|
7749
|
-
return;
|
|
7750
|
-
}
|
|
7751
7746
|
const parsedTimeout = Number.parseInt(
|
|
7752
|
-
process.env[RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV] ?? "600000",
|
|
7747
|
+
String(options.timeoutMs ?? process.env[RUDDER_POSTGRES_RUNTIME_DOWNLOAD_TIMEOUT_MS_ENV] ?? "600000"),
|
|
7753
7748
|
10
|
|
7754
7749
|
);
|
|
7755
7750
|
const controller = new AbortController();
|
|
7756
7751
|
const timeout = Number.isFinite(parsedTimeout) && parsedTimeout > 0 ? setTimeout(() => controller.abort(), parsedTimeout) : null;
|
|
7757
7752
|
try {
|
|
7758
|
-
const response = await fetch(url, { signal: controller.signal });
|
|
7759
|
-
if (!response.ok) {
|
|
7760
|
-
throw new Error(`failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
7761
|
-
}
|
|
7762
|
-
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
7763
|
-
if (Number.isSafeInteger(contentLength) && contentLength > maxBytes) {
|
|
7764
|
-
throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
|
|
7765
|
-
}
|
|
7766
|
-
if (!response.body) throw new Error("PostgreSQL runtime archive response has no body");
|
|
7767
7753
|
const hash = createHash2("sha256");
|
|
7768
7754
|
let bytes = 0;
|
|
7769
7755
|
const monitor = new Transform({
|
|
@@ -7777,15 +7763,39 @@ async function downloadRuntimePostgresArchive(url, targetPath, trustedSha256) {
|
|
|
7777
7763
|
callback(null, chunk);
|
|
7778
7764
|
}
|
|
7779
7765
|
});
|
|
7766
|
+
if (url.startsWith("file://")) {
|
|
7767
|
+
const readStream = (options.createReadStreamImpl ?? createReadStream)(fileURLToPath4(url));
|
|
7768
|
+
await pipeline(readStream, monitor, createWriteStream(targetPath), { signal: controller.signal });
|
|
7769
|
+
if (expectedSha256) {
|
|
7770
|
+
const actual = hash.digest("hex");
|
|
7771
|
+
if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
|
|
7772
|
+
}
|
|
7773
|
+
return;
|
|
7774
|
+
}
|
|
7775
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
7776
|
+
if (!response.ok) {
|
|
7777
|
+
throw new Error(`failed to download ${url}: ${response.status} ${response.statusText}`);
|
|
7778
|
+
}
|
|
7779
|
+
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
7780
|
+
if (Number.isSafeInteger(contentLength) && contentLength > maxBytes) {
|
|
7781
|
+
throw new Error(`PostgreSQL runtime archive exceeds ${maxBytes} bytes`);
|
|
7782
|
+
}
|
|
7783
|
+
if (!response.body) throw new Error("PostgreSQL runtime archive response has no body");
|
|
7780
7784
|
await pipeline(
|
|
7781
7785
|
Readable.fromWeb(response.body),
|
|
7782
7786
|
monitor,
|
|
7783
|
-
createWriteStream(targetPath, { flags: "wx" })
|
|
7787
|
+
createWriteStream(targetPath, { flags: "wx" }),
|
|
7788
|
+
{ signal: controller.signal }
|
|
7784
7789
|
);
|
|
7785
7790
|
if (expectedSha256) {
|
|
7786
7791
|
const actual = hash.digest("hex");
|
|
7787
7792
|
if (actual !== expectedSha256) throw new Error(`PostgreSQL runtime archive SHA-256 mismatch: expected ${expectedSha256}, got ${actual}`);
|
|
7788
7793
|
}
|
|
7794
|
+
} catch (error) {
|
|
7795
|
+
if (controller.signal.aborted) {
|
|
7796
|
+
throw new Error(`PostgreSQL runtime archive download timed out after ${parsedTimeout}ms`, { cause: error });
|
|
7797
|
+
}
|
|
7798
|
+
throw error;
|
|
7789
7799
|
} finally {
|
|
7790
7800
|
if (timeout) clearTimeout(timeout);
|
|
7791
7801
|
}
|
|
@@ -7840,10 +7850,45 @@ var init_postgres_runtime_source = __esm({
|
|
|
7840
7850
|
|
|
7841
7851
|
// src/runtime/install.ts
|
|
7842
7852
|
import { execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
7843
|
-
import {
|
|
7853
|
+
import { createReadStream as createReadStream2, createWriteStream as createWriteStream2 } from "node:fs";
|
|
7854
|
+
import { chmod, mkdir, mkdtemp, readFile, readdir, realpath, rename, rm, stat, symlink, writeFile } from "node:fs/promises";
|
|
7844
7855
|
import { createRequire } from "node:module";
|
|
7845
|
-
import
|
|
7856
|
+
import path9 from "node:path";
|
|
7857
|
+
import { performance as performance2 } from "node:perf_hooks";
|
|
7858
|
+
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
7846
7859
|
import { pathToFileURL } from "node:url";
|
|
7860
|
+
function createRuntimeInstallDeadline(options) {
|
|
7861
|
+
if (options.timeoutMs === void 0) return void 0;
|
|
7862
|
+
if (!Number.isFinite(options.timeoutMs) || options.timeoutMs <= 0) {
|
|
7863
|
+
throw new Error("Runtime installation timeout must be a positive number of milliseconds.");
|
|
7864
|
+
}
|
|
7865
|
+
const now = options.now ?? (() => performance2.now());
|
|
7866
|
+
return { expiresAt: now() + options.timeoutMs, now };
|
|
7867
|
+
}
|
|
7868
|
+
function remainingRuntimeInstallMs(deadline, cacheDir, command) {
|
|
7869
|
+
if (!deadline) return void 0;
|
|
7870
|
+
const remaining = Math.ceil(deadline.expiresAt - deadline.now());
|
|
7871
|
+
if (remaining <= 0) {
|
|
7872
|
+
throw new RuntimeInstallError(
|
|
7873
|
+
`Timed out while preparing the Rudder runtime during ${command}`,
|
|
7874
|
+
{ cacheDir, command }
|
|
7875
|
+
);
|
|
7876
|
+
}
|
|
7877
|
+
return remaining;
|
|
7878
|
+
}
|
|
7879
|
+
function runtimeInstallDeadlineError(cacheDir, command) {
|
|
7880
|
+
return new RuntimeInstallError(
|
|
7881
|
+
`Timed out while preparing the Rudder runtime during ${command}`,
|
|
7882
|
+
{ cacheDir, command }
|
|
7883
|
+
);
|
|
7884
|
+
}
|
|
7885
|
+
function isRuntimeInstallDeadlineError(error) {
|
|
7886
|
+
return error instanceof RuntimeInstallError && error.message.startsWith("Timed out while preparing the Rudder runtime during ");
|
|
7887
|
+
}
|
|
7888
|
+
function isChildProcessTimeoutError(error) {
|
|
7889
|
+
const detail = error;
|
|
7890
|
+
return detail?.code === "ETIMEDOUT" || detail?.killed === true || detail?.signal === "SIGTERM";
|
|
7891
|
+
}
|
|
7847
7892
|
function sanitizeRuntimeCacheSegment(value) {
|
|
7848
7893
|
return encodeURIComponent(value.trim() || "latest").replaceAll("%", "_");
|
|
7849
7894
|
}
|
|
@@ -7852,7 +7897,7 @@ function resolveRuntimePackageVersion(version) {
|
|
|
7852
7897
|
return normalized.length > 0 ? normalized : "latest";
|
|
7853
7898
|
}
|
|
7854
7899
|
function resolveRuntimeCacheDir(version, homeDir = resolveRudderHomeDir()) {
|
|
7855
|
-
return
|
|
7900
|
+
return path9.join(homeDir, "runtimes", sanitizeRuntimeCacheSegment(resolveRuntimePackageVersion(version)));
|
|
7856
7901
|
}
|
|
7857
7902
|
function resolveRuntimePackageSpec(version, packageName = RUNTIME_NPM_PACKAGE_NAME) {
|
|
7858
7903
|
const packageVersion = resolveRuntimePackageVersion(version);
|
|
@@ -7860,7 +7905,7 @@ function resolveRuntimePackageSpec(version, packageName = RUNTIME_NPM_PACKAGE_NA
|
|
|
7860
7905
|
}
|
|
7861
7906
|
async function readRuntimeInstallMetadata(cacheDir) {
|
|
7862
7907
|
try {
|
|
7863
|
-
const raw = await readFile(
|
|
7908
|
+
const raw = await readFile(path9.join(cacheDir, RUNTIME_METADATA_FILE), "utf8");
|
|
7864
7909
|
const parsed = JSON.parse(raw);
|
|
7865
7910
|
if (parsed.version !== 1) return null;
|
|
7866
7911
|
if (typeof parsed.packageName !== "string" || typeof parsed.packageVersion !== "string") return null;
|
|
@@ -7875,7 +7920,7 @@ async function readRuntimeInstallMetadata(cacheDir) {
|
|
|
7875
7920
|
}
|
|
7876
7921
|
}
|
|
7877
7922
|
async function writeRuntimeInstallMetadata(cacheDir, metadata) {
|
|
7878
|
-
await writeFile(
|
|
7923
|
+
await writeFile(path9.join(cacheDir, RUNTIME_METADATA_FILE), `${JSON.stringify(metadata, null, 2)}
|
|
7879
7924
|
`, "utf8");
|
|
7880
7925
|
}
|
|
7881
7926
|
async function touchRuntimeInstallMetadata(cacheDir, postgresRuntime) {
|
|
@@ -7903,27 +7948,28 @@ function resolveEmbeddedPostgresPlatformPackage(platform = process.platform, arc
|
|
|
7903
7948
|
}
|
|
7904
7949
|
async function canResolveRuntimePackage(cacheDir, packageName) {
|
|
7905
7950
|
try {
|
|
7906
|
-
await readFile(
|
|
7951
|
+
await readFile(path9.join(cacheDir, "node_modules", ...packageName.split("/"), "package.json"), "utf8");
|
|
7907
7952
|
return true;
|
|
7908
7953
|
} catch {
|
|
7909
7954
|
return false;
|
|
7910
7955
|
}
|
|
7911
7956
|
}
|
|
7912
|
-
async function hasRequiredRuntimePlatformDependencies(cacheDir, metadata, postgresVersionProbe) {
|
|
7957
|
+
async function hasRequiredRuntimePlatformDependencies(cacheDir, metadata, postgresVersionProbe, deadline) {
|
|
7913
7958
|
if (!await canResolveRuntimePackage(cacheDir, EMBEDDED_POSTGRES_PACKAGE_NAME)) return true;
|
|
7914
7959
|
const platformPackage = resolveEmbeddedPostgresPlatformPackage();
|
|
7915
7960
|
if (!platformPackage) return true;
|
|
7916
7961
|
if (await canResolveRuntimePackage(cacheDir, platformPackage)) return true;
|
|
7917
7962
|
const expectedSharedBinDir = resolveSharedRuntimePostgresPayloadBinDir(
|
|
7918
|
-
|
|
7963
|
+
path9.dirname(path9.dirname(cacheDir))
|
|
7919
7964
|
);
|
|
7920
|
-
return metadata.postgresRuntime?.scope === "shared" && metadata.postgresRuntime.platform === process.platform && metadata.postgresRuntime.arch === process.arch &&
|
|
7965
|
+
return metadata.postgresRuntime?.scope === "shared" && metadata.postgresRuntime.platform === process.platform && metadata.postgresRuntime.arch === process.arch && path9.resolve(metadata.postgresRuntime.binDir) === path9.resolve(expectedSharedBinDir) && await isRuntimePostgresPayloadUsable(
|
|
7921
7966
|
cacheDir,
|
|
7922
7967
|
metadata.postgresRuntime.binDir,
|
|
7923
|
-
postgresVersionProbe
|
|
7968
|
+
postgresVersionProbe,
|
|
7969
|
+
deadline
|
|
7924
7970
|
);
|
|
7925
7971
|
}
|
|
7926
|
-
async function
|
|
7972
|
+
async function isRuntimeCacheHitWithinDeadline(options, deadline) {
|
|
7927
7973
|
const packageName = options.packageName ?? RUNTIME_NPM_PACKAGE_NAME;
|
|
7928
7974
|
const packageVersion = resolveRuntimePackageVersion(options.version);
|
|
7929
7975
|
const metadata = await readRuntimeInstallMetadata(options.cacheDir);
|
|
@@ -7931,15 +7977,17 @@ async function isRuntimeCacheHit(options) {
|
|
|
7931
7977
|
return false;
|
|
7932
7978
|
}
|
|
7933
7979
|
try {
|
|
7934
|
-
const packageJsonPath =
|
|
7980
|
+
const packageJsonPath = path9.join(options.cacheDir, "node_modules", ...packageName.split("/"), "package.json");
|
|
7935
7981
|
const packageJson = JSON.parse(await readFile(packageJsonPath, "utf8"));
|
|
7936
7982
|
const packageVersionMatches = packageVersion === "latest" || packageJson.version === packageVersion;
|
|
7937
7983
|
return packageVersionMatches && await hasRequiredRuntimePlatformDependencies(
|
|
7938
7984
|
options.cacheDir,
|
|
7939
7985
|
metadata,
|
|
7940
|
-
options.postgresVersionProbe ?? readPostgresVersion
|
|
7986
|
+
options.postgresVersionProbe ?? readPostgresVersion,
|
|
7987
|
+
deadline
|
|
7941
7988
|
);
|
|
7942
|
-
} catch {
|
|
7989
|
+
} catch (error) {
|
|
7990
|
+
if (isRuntimeInstallDeadlineError(error)) throw error;
|
|
7943
7991
|
return false;
|
|
7944
7992
|
}
|
|
7945
7993
|
}
|
|
@@ -7947,15 +7995,45 @@ async function ensureRuntimeInstalled(options) {
|
|
|
7947
7995
|
const packageVersion = resolveRuntimePackageVersion(options.version);
|
|
7948
7996
|
const homeDir = options.homeDir ?? resolveRudderHomeDir();
|
|
7949
7997
|
const cacheDir = resolveRuntimeCacheDir(packageVersion, homeDir);
|
|
7998
|
+
const deadline = createRuntimeInstallDeadline(options);
|
|
7950
7999
|
return withRuntimeFilesystemLock(
|
|
7951
|
-
|
|
8000
|
+
path9.join(homeDir, "runtime-payloads", ".postgres-runtime.lifecycle.lock"),
|
|
7952
8001
|
async () => withRuntimeFilesystemLock(
|
|
7953
8002
|
`${cacheDir}.install.lock`,
|
|
7954
|
-
async () =>
|
|
7955
|
-
|
|
8003
|
+
async () => {
|
|
8004
|
+
try {
|
|
8005
|
+
return await ensureRuntimeInstalledUnlocked(options, deadline);
|
|
8006
|
+
} catch (error) {
|
|
8007
|
+
if (options.cleanupIncompleteOnFailure === true) {
|
|
8008
|
+
scheduleIncompleteRuntimeCacheCleanup({
|
|
8009
|
+
cacheDir,
|
|
8010
|
+
packageVersion,
|
|
8011
|
+
remove: options.removeIncompleteCache
|
|
8012
|
+
});
|
|
8013
|
+
}
|
|
8014
|
+
throw error;
|
|
8015
|
+
}
|
|
8016
|
+
},
|
|
8017
|
+
{ deadline, cacheDir, command: "acquire target runtime install lock" }
|
|
8018
|
+
),
|
|
8019
|
+
{ deadline, cacheDir, command: "acquire PostgreSQL runtime lifecycle lock" }
|
|
7956
8020
|
);
|
|
7957
8021
|
}
|
|
7958
|
-
|
|
8022
|
+
function scheduleIncompleteRuntimeCacheCleanup(options) {
|
|
8023
|
+
const remove = options.remove ?? ((cacheDir) => rm(cacheDir, { recursive: true, force: true }));
|
|
8024
|
+
void withRuntimeFilesystemLock(
|
|
8025
|
+
`${options.cacheDir}.install.lock`,
|
|
8026
|
+
async () => {
|
|
8027
|
+
const metadata = await readRuntimeInstallMetadata(options.cacheDir);
|
|
8028
|
+
if (!metadata || metadata.packageVersion !== options.packageVersion) {
|
|
8029
|
+
await remove(options.cacheDir);
|
|
8030
|
+
}
|
|
8031
|
+
},
|
|
8032
|
+
{ cacheDir: options.cacheDir, command: "cleanup incomplete target runtime cache" }
|
|
8033
|
+
).catch(() => {
|
|
8034
|
+
});
|
|
8035
|
+
}
|
|
8036
|
+
async function ensureRuntimeInstalledUnlocked(options, deadline) {
|
|
7959
8037
|
const packageName = options.packageName ?? RUNTIME_NPM_PACKAGE_NAME;
|
|
7960
8038
|
const packageVersion = resolveRuntimePackageVersion(options.version);
|
|
7961
8039
|
const homeDir = options.homeDir ?? resolveRudderHomeDir();
|
|
@@ -7963,14 +8041,32 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
7963
8041
|
const packageSpec = resolveRuntimePackageSpec(packageVersion, packageName);
|
|
7964
8042
|
const command = formatRuntimeInstallCommand(cacheDir, packageSpec);
|
|
7965
8043
|
const preparePostgresPayload = options.preparePostgresPayload === true;
|
|
7966
|
-
const postgresVersionProbe = options.postgresVersionProbe ??
|
|
7967
|
-
|
|
8044
|
+
const postgresVersionProbe = options.postgresVersionProbe ?? ((binaryPath) => {
|
|
8045
|
+
const probeCommand = `${binaryPath} --version`;
|
|
8046
|
+
try {
|
|
8047
|
+
return readPostgresVersion(
|
|
8048
|
+
binaryPath,
|
|
8049
|
+
remainingRuntimeInstallMs(deadline, cacheDir, probeCommand)
|
|
8050
|
+
);
|
|
8051
|
+
} catch (error) {
|
|
8052
|
+
if (isChildProcessTimeoutError(error)) {
|
|
8053
|
+
throw runtimeInstallDeadlineError(cacheDir, probeCommand);
|
|
8054
|
+
}
|
|
8055
|
+
throw error;
|
|
8056
|
+
}
|
|
8057
|
+
});
|
|
8058
|
+
if (await isRuntimeCacheHitWithinDeadline(
|
|
8059
|
+
{ cacheDir, version: packageVersion, packageName, postgresVersionProbe },
|
|
8060
|
+
deadline
|
|
8061
|
+
)) {
|
|
7968
8062
|
const postgresPayload2 = await stageRuntimePostgresPayload(
|
|
7969
8063
|
cacheDir,
|
|
7970
8064
|
homeDir,
|
|
7971
8065
|
packageVersion,
|
|
7972
8066
|
preparePostgresPayload,
|
|
7973
|
-
postgresVersionProbe
|
|
8067
|
+
postgresVersionProbe,
|
|
8068
|
+
deadline,
|
|
8069
|
+
options.cleanupPostgresDownloadWorkDir
|
|
7974
8070
|
);
|
|
7975
8071
|
await touchRuntimeInstallMetadata(cacheDir, postgresPayload2.metadata);
|
|
7976
8072
|
const prune2 = await maybePruneRuntimeCache({
|
|
@@ -7989,7 +8085,8 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
7989
8085
|
spawnSyncImpl,
|
|
7990
8086
|
cacheDir,
|
|
7991
8087
|
packageName,
|
|
7992
|
-
packageVersion
|
|
8088
|
+
packageVersion,
|
|
8089
|
+
deadline
|
|
7993
8090
|
});
|
|
7994
8091
|
if (existingRuntimeOutput !== null) {
|
|
7995
8092
|
const postgresPayload2 = await stageRuntimePostgresPayload(
|
|
@@ -7997,7 +8094,9 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
7997
8094
|
homeDir,
|
|
7998
8095
|
packageVersion,
|
|
7999
8096
|
preparePostgresPayload,
|
|
8000
|
-
postgresVersionProbe
|
|
8097
|
+
postgresVersionProbe,
|
|
8098
|
+
deadline,
|
|
8099
|
+
options.cleanupPostgresDownloadWorkDir
|
|
8001
8100
|
);
|
|
8002
8101
|
const metadata2 = {
|
|
8003
8102
|
version: 1,
|
|
@@ -8020,30 +8119,32 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
8020
8119
|
);
|
|
8021
8120
|
}
|
|
8022
8121
|
await rm(cacheDir, { recursive: true, force: true });
|
|
8023
|
-
await
|
|
8024
|
-
await writeFile(
|
|
8122
|
+
await mkdir(cacheDir, { recursive: true });
|
|
8123
|
+
await writeFile(path9.join(cacheDir, "package.json"), `${JSON.stringify(RUNTIME_CACHE_PACKAGE_JSON, null, 2)}
|
|
8025
8124
|
`, "utf8");
|
|
8026
|
-
const result = runNpmRuntimeInstall(spawnSyncImpl, cacheDir, packageSpec);
|
|
8125
|
+
const result = runNpmRuntimeInstall(spawnSyncImpl, cacheDir, packageSpec, deadline);
|
|
8027
8126
|
let output = collectSpawnOutput(result);
|
|
8028
|
-
if (result.status !== 0 && packageVersion !== "latest" && isVersionNotFoundError(output)) {
|
|
8127
|
+
if (result.status !== 0 && packageVersion !== "latest" && options.allowLatestFallback !== false && isVersionNotFoundError(output)) {
|
|
8029
8128
|
const fallbackVersion = "latest";
|
|
8030
8129
|
const fallbackCacheDir = resolveRuntimeCacheDir(fallbackVersion, options.homeDir);
|
|
8031
8130
|
const fallbackSpec = resolveRuntimePackageSpec(fallbackVersion, packageName);
|
|
8032
8131
|
const fallbackInstallResult = await withRuntimeFilesystemLock(
|
|
8033
8132
|
`${fallbackCacheDir}.install.lock`,
|
|
8034
8133
|
async () => {
|
|
8035
|
-
if (await
|
|
8134
|
+
if (await isRuntimeCacheHitWithinDeadline({
|
|
8036
8135
|
cacheDir: fallbackCacheDir,
|
|
8037
8136
|
version: fallbackVersion,
|
|
8038
8137
|
packageName,
|
|
8039
8138
|
postgresVersionProbe
|
|
8040
|
-
})) {
|
|
8139
|
+
}, deadline)) {
|
|
8041
8140
|
const fallbackPostgresPayload = await stageRuntimePostgresPayload(
|
|
8042
8141
|
fallbackCacheDir,
|
|
8043
8142
|
homeDir,
|
|
8044
8143
|
fallbackVersion,
|
|
8045
8144
|
preparePostgresPayload,
|
|
8046
|
-
postgresVersionProbe
|
|
8145
|
+
postgresVersionProbe,
|
|
8146
|
+
deadline,
|
|
8147
|
+
options.cleanupPostgresDownloadWorkDir
|
|
8047
8148
|
);
|
|
8048
8149
|
await touchRuntimeInstallMetadata(fallbackCacheDir, fallbackPostgresPayload.metadata);
|
|
8049
8150
|
return withPostgresPayload(
|
|
@@ -8058,22 +8159,24 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
8058
8159
|
);
|
|
8059
8160
|
}
|
|
8060
8161
|
await rm(fallbackCacheDir, { recursive: true, force: true });
|
|
8061
|
-
await
|
|
8062
|
-
await writeFile(
|
|
8162
|
+
await mkdir(fallbackCacheDir, { recursive: true });
|
|
8163
|
+
await writeFile(path9.join(fallbackCacheDir, "package.json"), `${JSON.stringify(RUNTIME_CACHE_PACKAGE_JSON, null, 2)}
|
|
8063
8164
|
`, "utf8");
|
|
8064
|
-
const fallbackResult = runNpmRuntimeInstall(spawnSyncImpl, fallbackCacheDir, fallbackSpec);
|
|
8165
|
+
const fallbackResult = runNpmRuntimeInstall(spawnSyncImpl, fallbackCacheDir, fallbackSpec, deadline);
|
|
8065
8166
|
let fallbackOutput = collectSpawnOutput(fallbackResult);
|
|
8066
8167
|
if (fallbackResult.status !== 0) return null;
|
|
8067
8168
|
fallbackOutput = collectOutputParts(
|
|
8068
8169
|
fallbackOutput,
|
|
8069
|
-
await ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, fallbackCacheDir)
|
|
8170
|
+
await ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, fallbackCacheDir, deadline)
|
|
8070
8171
|
);
|
|
8071
8172
|
const postgresPayload2 = await stageRuntimePostgresPayload(
|
|
8072
8173
|
fallbackCacheDir,
|
|
8073
8174
|
homeDir,
|
|
8074
8175
|
fallbackVersion,
|
|
8075
8176
|
preparePostgresPayload,
|
|
8076
|
-
postgresVersionProbe
|
|
8177
|
+
postgresVersionProbe,
|
|
8178
|
+
deadline,
|
|
8179
|
+
options.cleanupPostgresDownloadWorkDir
|
|
8077
8180
|
);
|
|
8078
8181
|
const fallbackMetadata = {
|
|
8079
8182
|
version: 1,
|
|
@@ -8094,7 +8197,8 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
8094
8197
|
},
|
|
8095
8198
|
postgresPayload2
|
|
8096
8199
|
);
|
|
8097
|
-
}
|
|
8200
|
+
},
|
|
8201
|
+
{ deadline, cacheDir: fallbackCacheDir, command: "acquire fallback runtime install lock" }
|
|
8098
8202
|
);
|
|
8099
8203
|
if (fallbackInstallResult) return fallbackInstallResult;
|
|
8100
8204
|
}
|
|
@@ -8106,14 +8210,16 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
8106
8210
|
}
|
|
8107
8211
|
output = collectOutputParts(
|
|
8108
8212
|
output,
|
|
8109
|
-
await ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, cacheDir)
|
|
8213
|
+
await ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, cacheDir, deadline)
|
|
8110
8214
|
);
|
|
8111
8215
|
const postgresPayload = await stageRuntimePostgresPayload(
|
|
8112
8216
|
cacheDir,
|
|
8113
8217
|
homeDir,
|
|
8114
8218
|
packageVersion,
|
|
8115
8219
|
preparePostgresPayload,
|
|
8116
|
-
postgresVersionProbe
|
|
8220
|
+
postgresVersionProbe,
|
|
8221
|
+
deadline,
|
|
8222
|
+
options.cleanupPostgresDownloadWorkDir
|
|
8117
8223
|
);
|
|
8118
8224
|
const metadata = {
|
|
8119
8225
|
version: 1,
|
|
@@ -8136,10 +8242,10 @@ async function ensureRuntimeInstalledUnlocked(options) {
|
|
|
8136
8242
|
);
|
|
8137
8243
|
}
|
|
8138
8244
|
function resolveRuntimePostgresPayloadBinDir(cacheDir, platform = process.platform, arch = process.arch) {
|
|
8139
|
-
return
|
|
8245
|
+
return path9.join(cacheDir, RUNTIME_POSTGRES_PAYLOAD_DIR, runtimePostgresPlatformSegment(platform, arch), "bin");
|
|
8140
8246
|
}
|
|
8141
8247
|
function resolveSharedRuntimePostgresPayloadBinDir(homeDir = resolveRudderHomeDir(), platform = process.platform, arch = process.arch) {
|
|
8142
|
-
return
|
|
8248
|
+
return path9.join(
|
|
8143
8249
|
homeDir,
|
|
8144
8250
|
"runtime-payloads",
|
|
8145
8251
|
RUNTIME_POSTGRES_PAYLOAD_DIR,
|
|
@@ -8148,19 +8254,21 @@ function resolveSharedRuntimePostgresPayloadBinDir(homeDir = resolveRudderHomeDi
|
|
|
8148
8254
|
);
|
|
8149
8255
|
}
|
|
8150
8256
|
function resolveRuntimeServerEntrypoint(cacheDir, packageName = RUNTIME_NPM_PACKAGE_NAME) {
|
|
8151
|
-
return createRequire(
|
|
8257
|
+
return createRequire(path9.join(cacheDir, "package.json")).resolve(packageName);
|
|
8152
8258
|
}
|
|
8153
8259
|
async function importRuntimeServerModule(cacheDir, packageName = RUNTIME_NPM_PACKAGE_NAME) {
|
|
8154
8260
|
const entrypoint = resolveRuntimeServerEntrypoint(cacheDir, packageName);
|
|
8155
8261
|
return await import(pathToFileURL(entrypoint).href);
|
|
8156
8262
|
}
|
|
8157
|
-
function runNpmRuntimeInstall(spawnSyncImpl, cacheDir, packageSpec) {
|
|
8263
|
+
function runNpmRuntimeInstall(spawnSyncImpl, cacheDir, packageSpec, deadline) {
|
|
8264
|
+
const timeout = remainingRuntimeInstallMs(deadline, cacheDir, `npm install ${packageSpec}`);
|
|
8158
8265
|
return spawnSyncImpl(
|
|
8159
8266
|
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
8160
8267
|
["install", "--prefix", cacheDir, ...RUNTIME_NPM_INSTALL_FLAGS, packageSpec],
|
|
8161
8268
|
{
|
|
8162
8269
|
encoding: "utf8",
|
|
8163
8270
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8271
|
+
...timeout === void 0 ? {} : { timeout },
|
|
8164
8272
|
...process.platform === "win32" ? { shell: true, windowsHide: true } : {}
|
|
8165
8273
|
}
|
|
8166
8274
|
);
|
|
@@ -8169,7 +8277,7 @@ function formatRuntimeInstallCommand(cacheDir, packageSpec) {
|
|
|
8169
8277
|
return `npm install --prefix ${cacheDir} ${RUNTIME_NPM_INSTALL_FLAGS.join(" ")} ${packageSpec}`;
|
|
8170
8278
|
}
|
|
8171
8279
|
function formatRuntimePlatformRepairCommand(cacheDir, packageSpec) {
|
|
8172
|
-
return `npm pack ${packageSpec} --registry=${NPM_PUBLIC_REGISTRY_URL} --silent, then extract it into ${
|
|
8280
|
+
return `npm pack ${packageSpec} --registry=${NPM_PUBLIC_REGISTRY_URL} --silent, then extract it into ${path9.join(cacheDir, "node_modules")}`;
|
|
8173
8281
|
}
|
|
8174
8282
|
function collectSpawnOutput(result) {
|
|
8175
8283
|
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();
|
|
@@ -8186,7 +8294,7 @@ function withPostgresPayload(result, postgresPayload) {
|
|
|
8186
8294
|
};
|
|
8187
8295
|
}
|
|
8188
8296
|
function runtimePackageJsonPath(cacheDir, packageName) {
|
|
8189
|
-
return
|
|
8297
|
+
return path9.join(cacheDir, "node_modules", ...packageName.split("/"), "package.json");
|
|
8190
8298
|
}
|
|
8191
8299
|
async function readRuntimePackageJson(cacheDir, packageName) {
|
|
8192
8300
|
try {
|
|
@@ -8199,7 +8307,11 @@ async function tryRepairExistingRuntimePackage(options) {
|
|
|
8199
8307
|
const runtimePackage = await readRuntimePackageJson(options.cacheDir, options.packageName);
|
|
8200
8308
|
if (!runtimePackage) return null;
|
|
8201
8309
|
if (options.packageVersion !== "latest" && runtimePackage.version !== options.packageVersion) return null;
|
|
8202
|
-
const output = await ensureRequiredEmbeddedPostgresPlatformPackage(
|
|
8310
|
+
const output = await ensureRequiredEmbeddedPostgresPlatformPackage(
|
|
8311
|
+
options.spawnSyncImpl,
|
|
8312
|
+
options.cacheDir,
|
|
8313
|
+
options.deadline
|
|
8314
|
+
);
|
|
8203
8315
|
if (!await canResolveRuntimePackage(options.cacheDir, EMBEDDED_POSTGRES_PACKAGE_NAME)) return output;
|
|
8204
8316
|
const platformPackage = resolveEmbeddedPostgresPlatformPackage();
|
|
8205
8317
|
return !platformPackage || await canResolveRuntimePackage(options.cacheDir, platformPackage) ? output : null;
|
|
@@ -8213,13 +8325,19 @@ async function resolveEmbeddedPostgresPlatformPackageSpec(cacheDir) {
|
|
|
8213
8325
|
const packageVersion = normalizeOptionalDependencyVersion(versionRange);
|
|
8214
8326
|
return packageVersion ? `${packageName}@${packageVersion}` : packageName;
|
|
8215
8327
|
}
|
|
8216
|
-
async function ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, cacheDir) {
|
|
8328
|
+
async function ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, cacheDir, deadline) {
|
|
8217
8329
|
const packageSpec = await resolveEmbeddedPostgresPlatformPackageSpec(cacheDir);
|
|
8218
8330
|
if (!packageSpec) return "";
|
|
8219
8331
|
const packageName = packageNameFromSpec(packageSpec);
|
|
8220
8332
|
if (packageName && await canResolveRuntimePackage(cacheDir, packageName)) return "";
|
|
8221
8333
|
await removeRuntimeInstallLocks(cacheDir);
|
|
8222
|
-
const result = await installRuntimePackageInStaging(
|
|
8334
|
+
const result = await installRuntimePackageInStaging(
|
|
8335
|
+
spawnSyncImpl,
|
|
8336
|
+
cacheDir,
|
|
8337
|
+
packageSpec,
|
|
8338
|
+
packageName,
|
|
8339
|
+
deadline
|
|
8340
|
+
);
|
|
8223
8341
|
const output = collectSpawnOutput(result);
|
|
8224
8342
|
if (result.status === 0 && packageName && await canResolveRuntimePackage(cacheDir, packageName)) {
|
|
8225
8343
|
return output;
|
|
@@ -8230,28 +8348,29 @@ async function ensureRequiredEmbeddedPostgresPlatformPackage(spawnSyncImpl, cach
|
|
|
8230
8348
|
{ cacheDir, command, output }
|
|
8231
8349
|
);
|
|
8232
8350
|
}
|
|
8233
|
-
async function installRuntimePackageInStaging(spawnSyncImpl, cacheDir, packageSpec, packageName) {
|
|
8234
|
-
const stagingDir =
|
|
8235
|
-
await
|
|
8351
|
+
async function installRuntimePackageInStaging(spawnSyncImpl, cacheDir, packageSpec, packageName, deadline) {
|
|
8352
|
+
const stagingDir = path9.join(cacheDir, `.platform-repair-${process.pid}-${Date.now()}`);
|
|
8353
|
+
await mkdir(stagingDir, { recursive: true });
|
|
8236
8354
|
try {
|
|
8237
|
-
const packResult = runNpmPack(spawnSyncImpl, packageSpec, stagingDir);
|
|
8355
|
+
const packResult = runNpmPack(spawnSyncImpl, packageSpec, stagingDir, cacheDir, deadline);
|
|
8238
8356
|
if (packResult.status !== 0) return packResult;
|
|
8239
8357
|
const packFilename = parseNpmPackFilename(packResult.stdout);
|
|
8240
8358
|
if (!packFilename) {
|
|
8241
8359
|
return createSyntheticSpawnResult(1, "", `Unable to parse npm pack output for ${packageSpec}.`);
|
|
8242
8360
|
}
|
|
8243
|
-
const archivePath =
|
|
8244
|
-
const targetDir =
|
|
8245
|
-
await
|
|
8361
|
+
const archivePath = path9.join(stagingDir, packFilename);
|
|
8362
|
+
const targetDir = path9.dirname(runtimePackageJsonPath(cacheDir, packageName));
|
|
8363
|
+
await mkdir(path9.dirname(targetDir), { recursive: true });
|
|
8246
8364
|
await rm(targetDir, { recursive: true, force: true });
|
|
8247
|
-
await
|
|
8248
|
-
const extractResult = runTarExtract(spawnSyncImpl, archivePath, targetDir);
|
|
8365
|
+
await mkdir(targetDir, { recursive: true });
|
|
8366
|
+
const extractResult = runTarExtract(spawnSyncImpl, archivePath, targetDir, cacheDir, deadline);
|
|
8249
8367
|
return combineSpawnResults(packResult, extractResult);
|
|
8250
8368
|
} finally {
|
|
8251
8369
|
await rm(stagingDir, { recursive: true, force: true });
|
|
8252
8370
|
}
|
|
8253
8371
|
}
|
|
8254
|
-
function runNpmPack(spawnSyncImpl, packageSpec, destinationDir) {
|
|
8372
|
+
function runNpmPack(spawnSyncImpl, packageSpec, destinationDir, cacheDir, deadline) {
|
|
8373
|
+
const timeout = remainingRuntimeInstallMs(deadline, cacheDir, `npm pack ${packageSpec}`);
|
|
8255
8374
|
return spawnSyncImpl(
|
|
8256
8375
|
process.platform === "win32" ? "npm.cmd" : "npm",
|
|
8257
8376
|
["pack", packageSpec, "--pack-destination", destinationDir, ...RUNTIME_NPM_PACK_FLAGS],
|
|
@@ -8259,17 +8378,20 @@ function runNpmPack(spawnSyncImpl, packageSpec, destinationDir) {
|
|
|
8259
8378
|
encoding: "utf8",
|
|
8260
8379
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8261
8380
|
env: { ...process.env, ...NPM_PLATFORM_REPAIR_ENV },
|
|
8381
|
+
...timeout === void 0 ? {} : { timeout },
|
|
8262
8382
|
...process.platform === "win32" ? { shell: true, windowsHide: true } : {}
|
|
8263
8383
|
}
|
|
8264
8384
|
);
|
|
8265
8385
|
}
|
|
8266
|
-
function runTarExtract(spawnSyncImpl, archivePath, targetDir) {
|
|
8386
|
+
function runTarExtract(spawnSyncImpl, archivePath, targetDir, cacheDir, deadline) {
|
|
8387
|
+
const timeout = remainingRuntimeInstallMs(deadline, cacheDir, "extract runtime platform package");
|
|
8267
8388
|
return spawnSyncImpl(
|
|
8268
8389
|
"tar",
|
|
8269
8390
|
["-xzf", archivePath, "-C", targetDir, "--strip-components", "1"],
|
|
8270
8391
|
{
|
|
8271
8392
|
encoding: "utf8",
|
|
8272
8393
|
stdio: ["ignore", "pipe", "pipe"],
|
|
8394
|
+
...timeout === void 0 ? {} : { timeout },
|
|
8273
8395
|
...process.platform === "win32" ? { windowsHide: true } : {}
|
|
8274
8396
|
}
|
|
8275
8397
|
);
|
|
@@ -8293,8 +8415,8 @@ function combineSpawnResults(...results) {
|
|
|
8293
8415
|
}
|
|
8294
8416
|
async function removeRuntimeInstallLocks(cacheDir) {
|
|
8295
8417
|
await Promise.all([
|
|
8296
|
-
rm(
|
|
8297
|
-
rm(
|
|
8418
|
+
rm(path9.join(cacheDir, "package-lock.json"), { force: true }),
|
|
8419
|
+
rm(path9.join(cacheDir, "node_modules", ".package-lock.json"), { force: true })
|
|
8298
8420
|
]);
|
|
8299
8421
|
}
|
|
8300
8422
|
function packageNameFromSpec(packageSpec) {
|
|
@@ -8318,77 +8440,95 @@ function runtimePostgresExecutableName(baseName) {
|
|
|
8318
8440
|
return process.platform === "win32" ? `${baseName}.exe` : baseName;
|
|
8319
8441
|
}
|
|
8320
8442
|
function debianSharedirCandidate(binDir) {
|
|
8321
|
-
const normalized =
|
|
8322
|
-
const parts = normalized.split(
|
|
8443
|
+
const normalized = path9.resolve(binDir);
|
|
8444
|
+
const parts = normalized.split(path9.sep);
|
|
8323
8445
|
const libIndex = parts.lastIndexOf("lib");
|
|
8324
8446
|
if (libIndex < 0) return null;
|
|
8325
8447
|
if (parts[libIndex + 1] !== "postgresql") return null;
|
|
8326
8448
|
const version = parts[libIndex + 2];
|
|
8327
8449
|
if (!version || parts[libIndex + 3] !== "bin") return null;
|
|
8328
|
-
const prefix = parts.slice(0, libIndex).join(
|
|
8329
|
-
return
|
|
8450
|
+
const prefix = parts.slice(0, libIndex).join(path9.sep) || path9.sep;
|
|
8451
|
+
return path9.join(prefix, "share", "postgresql", version);
|
|
8330
8452
|
}
|
|
8331
|
-
async function resolveRuntimePostgresTemplateDir(binDir) {
|
|
8453
|
+
async function resolveRuntimePostgresTemplateDir(binDir, cacheDir = binDir, deadline) {
|
|
8332
8454
|
for (const candidatePath of [
|
|
8333
|
-
|
|
8334
|
-
|
|
8455
|
+
path9.join(binDir, "..", "share", "postgresql", "postgres.bki"),
|
|
8456
|
+
path9.join(binDir, "..", "share", "postgres.bki")
|
|
8335
8457
|
]) {
|
|
8458
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "discover PostgreSQL runtime templates");
|
|
8336
8459
|
try {
|
|
8337
|
-
await
|
|
8338
|
-
return
|
|
8460
|
+
await stat(candidatePath);
|
|
8461
|
+
return path9.dirname(candidatePath);
|
|
8339
8462
|
} catch {
|
|
8340
8463
|
}
|
|
8341
8464
|
}
|
|
8342
8465
|
const debianSharedir = debianSharedirCandidate(binDir);
|
|
8343
8466
|
if (debianSharedir) {
|
|
8467
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "discover PostgreSQL runtime templates");
|
|
8344
8468
|
try {
|
|
8345
|
-
await
|
|
8469
|
+
await stat(path9.join(debianSharedir, "postgres.bki"));
|
|
8346
8470
|
return debianSharedir;
|
|
8347
8471
|
} catch {
|
|
8348
8472
|
}
|
|
8349
8473
|
}
|
|
8350
|
-
const pgConfigPath =
|
|
8474
|
+
const pgConfigPath = path9.join(binDir, process.platform === "win32" ? "pg_config.exe" : "pg_config");
|
|
8351
8475
|
try {
|
|
8352
|
-
|
|
8353
|
-
|
|
8476
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "discover PostgreSQL runtime templates");
|
|
8477
|
+
await stat(pgConfigPath);
|
|
8478
|
+
const timeout = remainingRuntimeInstallMs(deadline, cacheDir, `${pgConfigPath} --sharedir`);
|
|
8479
|
+
let sharedir;
|
|
8480
|
+
try {
|
|
8481
|
+
sharedir = execFileSync(pgConfigPath, ["--sharedir"], {
|
|
8482
|
+
encoding: "utf8",
|
|
8483
|
+
...timeout === void 0 ? {} : { timeout }
|
|
8484
|
+
}).trim();
|
|
8485
|
+
} catch (error) {
|
|
8486
|
+
if (isChildProcessTimeoutError(error)) {
|
|
8487
|
+
throw runtimeInstallDeadlineError(cacheDir, `${pgConfigPath} --sharedir`);
|
|
8488
|
+
}
|
|
8489
|
+
throw error;
|
|
8490
|
+
}
|
|
8354
8491
|
if (!sharedir) return null;
|
|
8355
|
-
|
|
8356
|
-
|
|
8492
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "validate PostgreSQL runtime templates");
|
|
8493
|
+
const candidatePath = path9.join(sharedir, "postgres.bki");
|
|
8494
|
+
await stat(candidatePath);
|
|
8357
8495
|
return sharedir;
|
|
8358
|
-
} catch {
|
|
8496
|
+
} catch (error) {
|
|
8497
|
+
if (isRuntimeInstallDeadlineError(error)) throw error;
|
|
8359
8498
|
return null;
|
|
8360
8499
|
}
|
|
8361
8500
|
}
|
|
8362
8501
|
function resolveRuntimePostgresShareDir(binDir, templateDir) {
|
|
8363
|
-
const adjacentShareDir =
|
|
8502
|
+
const adjacentShareDir = path9.resolve(binDir, "..", "share");
|
|
8364
8503
|
return pathIsInside(templateDir, adjacentShareDir) ? adjacentShareDir : templateDir;
|
|
8365
8504
|
}
|
|
8366
|
-
async function assertRuntimePostgresBinDirComplete(cacheDir, binDir) {
|
|
8505
|
+
async function assertRuntimePostgresBinDirComplete(cacheDir, binDir, deadline) {
|
|
8367
8506
|
const requiredBinaries = ["initdb", "pg_ctl", "postgres"];
|
|
8368
8507
|
const missing = [];
|
|
8369
8508
|
for (const binary of requiredBinaries) {
|
|
8370
|
-
|
|
8509
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "validate PostgreSQL runtime binaries");
|
|
8510
|
+
const binaryPath = path9.join(binDir, runtimePostgresExecutableName(binary));
|
|
8371
8511
|
try {
|
|
8372
|
-
await
|
|
8512
|
+
await stat(binaryPath);
|
|
8373
8513
|
} catch {
|
|
8374
8514
|
missing.push(binaryPath);
|
|
8375
8515
|
}
|
|
8376
8516
|
}
|
|
8377
|
-
const templateDir = await resolveRuntimePostgresTemplateDir(binDir);
|
|
8517
|
+
const templateDir = await resolveRuntimePostgresTemplateDir(binDir, cacheDir, deadline);
|
|
8378
8518
|
if (!templateDir) {
|
|
8379
|
-
missing.push(
|
|
8519
|
+
missing.push(path9.join(binDir, "..", "share", "postgresql", "postgres.bki"));
|
|
8380
8520
|
} else {
|
|
8381
8521
|
try {
|
|
8382
|
-
await
|
|
8522
|
+
await stat(path9.join(templateDir, "postgresql.conf.sample"));
|
|
8383
8523
|
} catch {
|
|
8384
|
-
missing.push(
|
|
8524
|
+
missing.push(path9.join(templateDir, "postgresql.conf.sample"));
|
|
8385
8525
|
}
|
|
8386
8526
|
const shareDir = resolveRuntimePostgresShareDir(binDir, templateDir);
|
|
8387
8527
|
const hasTimezoneDir = (await Promise.all([
|
|
8388
|
-
|
|
8389
|
-
|
|
8390
|
-
].map((candidate) =>
|
|
8391
|
-
if (!hasTimezoneDir) missing.push(
|
|
8528
|
+
path9.join(templateDir, "timezone"),
|
|
8529
|
+
path9.join(shareDir, "timezone")
|
|
8530
|
+
].map((candidate) => stat(candidate).catch(() => null)))).some((candidate) => candidate?.isDirectory());
|
|
8531
|
+
if (!hasTimezoneDir) missing.push(path9.join(shareDir, "timezone"));
|
|
8392
8532
|
}
|
|
8393
8533
|
if (missing.length > 0) {
|
|
8394
8534
|
throw new RuntimeInstallError(
|
|
@@ -8397,31 +8537,124 @@ async function assertRuntimePostgresBinDirComplete(cacheDir, binDir) {
|
|
|
8397
8537
|
);
|
|
8398
8538
|
}
|
|
8399
8539
|
}
|
|
8400
|
-
function readPostgresVersion(postgresBinary) {
|
|
8401
|
-
return execFileSync(postgresBinary, ["--version"], {
|
|
8540
|
+
function readPostgresVersion(postgresBinary, timeout) {
|
|
8541
|
+
return execFileSync(postgresBinary, ["--version"], {
|
|
8542
|
+
encoding: "utf8",
|
|
8543
|
+
...timeout === void 0 ? {} : { timeout }
|
|
8544
|
+
});
|
|
8402
8545
|
}
|
|
8403
|
-
async function isRuntimePostgresPayloadUsable(cacheDir, binDir, postgresVersionProbe) {
|
|
8546
|
+
async function isRuntimePostgresPayloadUsable(cacheDir, binDir, postgresVersionProbe, deadline) {
|
|
8404
8547
|
try {
|
|
8405
|
-
await validateRuntimePostgresVersion(cacheDir, binDir, postgresVersionProbe);
|
|
8548
|
+
await validateRuntimePostgresVersion(cacheDir, binDir, postgresVersionProbe, deadline);
|
|
8406
8549
|
return true;
|
|
8407
|
-
} catch {
|
|
8550
|
+
} catch (error) {
|
|
8551
|
+
if (isRuntimeInstallDeadlineError(error)) throw error;
|
|
8408
8552
|
return false;
|
|
8409
8553
|
}
|
|
8410
8554
|
}
|
|
8411
8555
|
function pathIsInside(candidatePath, rootPath) {
|
|
8412
|
-
const relative =
|
|
8413
|
-
return relative === "" || relative !== ".." && !relative.startsWith(`..${
|
|
8556
|
+
const relative = path9.relative(path9.resolve(rootPath), path9.resolve(candidatePath));
|
|
8557
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(relative);
|
|
8558
|
+
}
|
|
8559
|
+
async function copyRuntimePayloadEntry(sourcePath, targetPath, cacheDir, deadline, signal, command) {
|
|
8560
|
+
if (signal.aborted) throw runtimeInstallDeadlineError(cacheDir, command);
|
|
8561
|
+
remainingRuntimeInstallMs(deadline, cacheDir, command);
|
|
8562
|
+
const sourceStats = await stat(sourcePath);
|
|
8563
|
+
if (signal.aborted) throw runtimeInstallDeadlineError(cacheDir, command);
|
|
8564
|
+
remainingRuntimeInstallMs(deadline, cacheDir, command);
|
|
8565
|
+
if (sourceStats.isDirectory()) {
|
|
8566
|
+
await mkdir(targetPath, { recursive: true });
|
|
8567
|
+
const entries = await readdir(sourcePath);
|
|
8568
|
+
for (const entry of entries) {
|
|
8569
|
+
await copyRuntimePayloadEntry(
|
|
8570
|
+
path9.join(sourcePath, entry),
|
|
8571
|
+
path9.join(targetPath, entry),
|
|
8572
|
+
cacheDir,
|
|
8573
|
+
deadline,
|
|
8574
|
+
signal,
|
|
8575
|
+
command
|
|
8576
|
+
);
|
|
8577
|
+
}
|
|
8578
|
+
return;
|
|
8579
|
+
}
|
|
8580
|
+
if (!sourceStats.isFile()) return;
|
|
8581
|
+
await mkdir(path9.dirname(targetPath), { recursive: true });
|
|
8582
|
+
try {
|
|
8583
|
+
await pipeline2(
|
|
8584
|
+
createReadStream2(sourcePath),
|
|
8585
|
+
createWriteStream2(targetPath, { flags: "w", mode: sourceStats.mode }),
|
|
8586
|
+
{ signal }
|
|
8587
|
+
);
|
|
8588
|
+
await chmod(targetPath, sourceStats.mode);
|
|
8589
|
+
remainingRuntimeInstallMs(deadline, cacheDir, command);
|
|
8590
|
+
} catch (error) {
|
|
8591
|
+
await rm(targetPath, { force: true });
|
|
8592
|
+
if (signal.aborted || isRuntimeInstallDeadlineError(error)) {
|
|
8593
|
+
throw runtimeInstallDeadlineError(cacheDir, command);
|
|
8594
|
+
}
|
|
8595
|
+
throw error;
|
|
8596
|
+
}
|
|
8597
|
+
}
|
|
8598
|
+
async function copyRuntimePostgresPayloadWithinDeadline(sourceRuntimeDir, targetRuntimeDir, sourceShareDir, cacheDir, deadline, externalSignal) {
|
|
8599
|
+
const command = "copy PostgreSQL runtime payload";
|
|
8600
|
+
const timeoutMs = remainingRuntimeInstallMs(deadline, cacheDir, command);
|
|
8601
|
+
const controller = new AbortController();
|
|
8602
|
+
const abortFromExternal = () => controller.abort(externalSignal?.reason);
|
|
8603
|
+
if (externalSignal?.aborted) abortFromExternal();
|
|
8604
|
+
else externalSignal?.addEventListener("abort", abortFromExternal, { once: true });
|
|
8605
|
+
let timer;
|
|
8606
|
+
const timeoutPromise = timeoutMs === void 0 ? null : new Promise((_resolve, reject) => {
|
|
8607
|
+
timer = setTimeout(() => {
|
|
8608
|
+
controller.abort();
|
|
8609
|
+
reject(runtimeInstallDeadlineError(cacheDir, command));
|
|
8610
|
+
}, timeoutMs);
|
|
8611
|
+
});
|
|
8612
|
+
const copyPromise = (async () => {
|
|
8613
|
+
await mkdir(targetRuntimeDir, { recursive: true });
|
|
8614
|
+
for (const directoryName of ["bin", "lib"]) {
|
|
8615
|
+
const sourceDirectory = path9.join(sourceRuntimeDir, directoryName);
|
|
8616
|
+
if (!await stat(sourceDirectory).catch(() => null)) continue;
|
|
8617
|
+
await copyRuntimePayloadEntry(
|
|
8618
|
+
sourceDirectory,
|
|
8619
|
+
path9.join(targetRuntimeDir, directoryName),
|
|
8620
|
+
cacheDir,
|
|
8621
|
+
deadline,
|
|
8622
|
+
controller.signal,
|
|
8623
|
+
command
|
|
8624
|
+
);
|
|
8625
|
+
}
|
|
8626
|
+
await copyRuntimePayloadEntry(
|
|
8627
|
+
sourceShareDir,
|
|
8628
|
+
path9.join(targetRuntimeDir, "share"),
|
|
8629
|
+
cacheDir,
|
|
8630
|
+
deadline,
|
|
8631
|
+
controller.signal,
|
|
8632
|
+
command
|
|
8633
|
+
);
|
|
8634
|
+
})();
|
|
8635
|
+
try {
|
|
8636
|
+
if (timeoutPromise) await Promise.race([copyPromise, timeoutPromise]);
|
|
8637
|
+
else await copyPromise;
|
|
8638
|
+
} finally {
|
|
8639
|
+
if (timer) clearTimeout(timer);
|
|
8640
|
+
externalSignal?.removeEventListener("abort", abortFromExternal);
|
|
8641
|
+
}
|
|
8414
8642
|
}
|
|
8415
8643
|
async function withRuntimeFilesystemLock(lockPath, task, options = {}) {
|
|
8416
|
-
const
|
|
8644
|
+
const deadlineTimeout = remainingRuntimeInstallMs(
|
|
8645
|
+
options.deadline,
|
|
8646
|
+
options.cacheDir ?? path9.dirname(lockPath),
|
|
8647
|
+
options.command ?? `wait for runtime lock ${lockPath}`
|
|
8648
|
+
);
|
|
8649
|
+
const timeoutMs = Math.min(options.timeoutMs ?? 3e4, deadlineTimeout ?? Number.POSITIVE_INFINITY);
|
|
8417
8650
|
const pollMs = options.pollMs ?? 50;
|
|
8418
8651
|
const startedAt = Date.now();
|
|
8419
8652
|
const lockId = `${process.pid}-${startedAt}-${Math.random().toString(16).slice(2)}`;
|
|
8420
|
-
const ownerPath =
|
|
8421
|
-
await
|
|
8653
|
+
const ownerPath = path9.join(lockPath, "owner.json");
|
|
8654
|
+
await mkdir(path9.dirname(lockPath), { recursive: true });
|
|
8422
8655
|
while (true) {
|
|
8423
8656
|
try {
|
|
8424
|
-
await
|
|
8657
|
+
await mkdir(lockPath);
|
|
8425
8658
|
await writeFile(
|
|
8426
8659
|
ownerPath,
|
|
8427
8660
|
`${JSON.stringify({ pid: process.pid, lockId, createdAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
@@ -8438,7 +8671,7 @@ async function withRuntimeFilesystemLock(lockPath, task, options = {}) {
|
|
|
8438
8671
|
continue;
|
|
8439
8672
|
}
|
|
8440
8673
|
} catch {
|
|
8441
|
-
const lockStats = await
|
|
8674
|
+
const lockStats = await stat(lockPath).catch(() => null);
|
|
8442
8675
|
if (lockStats && Date.now() - lockStats.mtimeMs > 5e3) {
|
|
8443
8676
|
await rm(lockPath, { recursive: true, force: true });
|
|
8444
8677
|
continue;
|
|
@@ -8447,9 +8680,14 @@ async function withRuntimeFilesystemLock(lockPath, task, options = {}) {
|
|
|
8447
8680
|
if (Date.now() - startedAt >= timeoutMs) {
|
|
8448
8681
|
throw new RuntimeInstallError(
|
|
8449
8682
|
`Timed out waiting for PostgreSQL runtime install lock ${lockPath}`,
|
|
8450
|
-
{ cacheDir:
|
|
8683
|
+
{ cacheDir: path9.dirname(lockPath), command: "prepare shared PostgreSQL runtime", output: "" }
|
|
8451
8684
|
);
|
|
8452
8685
|
}
|
|
8686
|
+
remainingRuntimeInstallMs(
|
|
8687
|
+
options.deadline,
|
|
8688
|
+
options.cacheDir ?? path9.dirname(lockPath),
|
|
8689
|
+
options.command ?? `wait for runtime lock ${lockPath}`
|
|
8690
|
+
);
|
|
8453
8691
|
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
8454
8692
|
}
|
|
8455
8693
|
}
|
|
@@ -8467,44 +8705,45 @@ async function withRuntimeFilesystemLock(lockPath, task, options = {}) {
|
|
|
8467
8705
|
}
|
|
8468
8706
|
function isManagedRuntimePostgresBinDir(binDir, homeDir) {
|
|
8469
8707
|
const managedBinDir = process.env[RUDDER_DESKTOP_MANAGED_POSTGRES_BIN_DIR_ENV]?.trim();
|
|
8470
|
-
if (managedBinDir &&
|
|
8471
|
-
const runtimesRelative =
|
|
8472
|
-
|
|
8473
|
-
|
|
8708
|
+
if (managedBinDir && path9.resolve(managedBinDir) === path9.resolve(binDir)) return true;
|
|
8709
|
+
const runtimesRelative = path9.relative(
|
|
8710
|
+
path9.join(homeDir, "runtimes"),
|
|
8711
|
+
path9.resolve(binDir)
|
|
8474
8712
|
);
|
|
8475
|
-
const runtimeSegments = runtimesRelative.split(
|
|
8476
|
-
if (runtimesRelative !== "" && runtimesRelative !== ".." && !runtimesRelative.startsWith(`..${
|
|
8713
|
+
const runtimeSegments = runtimesRelative.split(path9.sep);
|
|
8714
|
+
if (runtimesRelative !== "" && runtimesRelative !== ".." && !runtimesRelative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(runtimesRelative) && runtimeSegments.length === 4 && runtimeSegments[0]?.length > 0 && runtimeSegments[1] === RUNTIME_POSTGRES_PAYLOAD_DIR && runtimeSegments[2] === `${process.platform}-${process.arch}` && runtimeSegments[3] === "bin") {
|
|
8477
8715
|
return true;
|
|
8478
8716
|
}
|
|
8479
|
-
const payloadsRelative =
|
|
8480
|
-
|
|
8481
|
-
|
|
8717
|
+
const payloadsRelative = path9.relative(
|
|
8718
|
+
path9.join(homeDir, "runtime-payloads"),
|
|
8719
|
+
path9.resolve(binDir)
|
|
8482
8720
|
);
|
|
8483
|
-
const payloadSegments = payloadsRelative.split(
|
|
8484
|
-
return payloadsRelative !== "" && payloadsRelative !== ".." && !payloadsRelative.startsWith(`..${
|
|
8721
|
+
const payloadSegments = payloadsRelative.split(path9.sep);
|
|
8722
|
+
return payloadsRelative !== "" && payloadsRelative !== ".." && !payloadsRelative.startsWith(`..${path9.sep}`) && !path9.isAbsolute(payloadsRelative) && payloadSegments.length === 3 && payloadSegments[0] === RUNTIME_POSTGRES_PAYLOAD_DIR && payloadSegments[1] === `${process.platform}-${process.arch}` && payloadSegments[2] === "bin";
|
|
8485
8723
|
}
|
|
8486
|
-
async function findLegacyRuntimePostgresBinDir(cacheDir, homeDir, postgresVersionProbe) {
|
|
8487
|
-
const runtimesRoot =
|
|
8724
|
+
async function findLegacyRuntimePostgresBinDir(cacheDir, homeDir, postgresVersionProbe, deadline) {
|
|
8725
|
+
const runtimesRoot = path9.join(homeDir, "runtimes");
|
|
8488
8726
|
const entries = await readdir(runtimesRoot, { withFileTypes: true }).catch(() => []);
|
|
8489
8727
|
for (const entry of entries) {
|
|
8490
8728
|
if (!entry.isDirectory() && !entry.isSymbolicLink()) continue;
|
|
8491
|
-
const candidateCacheDir =
|
|
8492
|
-
if (
|
|
8729
|
+
const candidateCacheDir = path9.join(runtimesRoot, entry.name);
|
|
8730
|
+
if (path9.resolve(candidateCacheDir) === path9.resolve(cacheDir)) continue;
|
|
8493
8731
|
const candidateBinDir = resolveRuntimePostgresPayloadBinDir(candidateCacheDir);
|
|
8494
|
-
|
|
8732
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "find cached PostgreSQL runtime payload");
|
|
8733
|
+
if (await isRuntimePostgresPayloadUsable(cacheDir, candidateBinDir, postgresVersionProbe, deadline)) {
|
|
8495
8734
|
return candidateBinDir;
|
|
8496
8735
|
}
|
|
8497
8736
|
}
|
|
8498
8737
|
return null;
|
|
8499
8738
|
}
|
|
8500
8739
|
async function readLiveRuntimeDescriptors(homeDir) {
|
|
8501
|
-
const instancesRoot =
|
|
8740
|
+
const instancesRoot = path9.join(homeDir, "instances");
|
|
8502
8741
|
const entries = await readdir(instancesRoot, { withFileTypes: true }).catch(() => []);
|
|
8503
8742
|
const descriptors = [];
|
|
8504
8743
|
for (const entry of entries) {
|
|
8505
8744
|
if (!entry.isDirectory()) continue;
|
|
8506
8745
|
try {
|
|
8507
|
-
const raw = JSON.parse(await readFile(
|
|
8746
|
+
const raw = JSON.parse(await readFile(path9.join(instancesRoot, entry.name, "runtime", "server.json"), "utf8"));
|
|
8508
8747
|
if (typeof raw.pid !== "number" || !Number.isInteger(raw.pid) || !isPidRunning(raw.pid) || typeof raw.version !== "string") {
|
|
8509
8748
|
continue;
|
|
8510
8749
|
}
|
|
@@ -8522,13 +8761,13 @@ async function assertSharedPostgresPayloadNotLive(cacheDir, homeDir, sharedBinDi
|
|
|
8522
8761
|
const sharedPhysicalBinDir = await realpath(sharedBinDir).catch(() => null);
|
|
8523
8762
|
const mayReferenceSharedPayload = await Promise.all(liveDescriptors.map(async (descriptor) => {
|
|
8524
8763
|
if (descriptor.postgresBinDir === void 0) return true;
|
|
8525
|
-
if (
|
|
8764
|
+
if (path9.resolve(descriptor.postgresBinDir) === path9.resolve(sharedBinDir)) return true;
|
|
8526
8765
|
const descriptorPhysicalBinDir = await realpath(descriptor.postgresBinDir).catch(() => null);
|
|
8527
|
-
if (descriptorPhysicalBinDir && sharedPhysicalBinDir &&
|
|
8766
|
+
if (descriptorPhysicalBinDir && sharedPhysicalBinDir && path9.resolve(descriptorPhysicalBinDir) === path9.resolve(sharedPhysicalBinDir)) {
|
|
8528
8767
|
return true;
|
|
8529
8768
|
}
|
|
8530
8769
|
if (!descriptorPhysicalBinDir) {
|
|
8531
|
-
return pathIsInside(descriptor.postgresBinDir,
|
|
8770
|
+
return pathIsInside(descriptor.postgresBinDir, path9.join(homeDir, "runtimes")) || pathIsInside(descriptor.postgresBinDir, path9.join(homeDir, "runtime-payloads"));
|
|
8532
8771
|
}
|
|
8533
8772
|
return false;
|
|
8534
8773
|
}));
|
|
@@ -8539,10 +8778,11 @@ async function assertSharedPostgresPayloadNotLive(cacheDir, homeDir, sharedBinDi
|
|
|
8539
8778
|
);
|
|
8540
8779
|
}
|
|
8541
8780
|
}
|
|
8542
|
-
async function validateRuntimePostgresVersion(cacheDir, binDir, postgresVersionProbe) {
|
|
8543
|
-
await assertRuntimePostgresBinDirComplete(cacheDir, binDir);
|
|
8781
|
+
async function validateRuntimePostgresVersion(cacheDir, binDir, postgresVersionProbe, deadline) {
|
|
8782
|
+
await assertRuntimePostgresBinDirComplete(cacheDir, binDir, deadline);
|
|
8544
8783
|
for (const binary of ["initdb", "pg_ctl", "postgres"]) {
|
|
8545
|
-
|
|
8784
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "validate PostgreSQL runtime version");
|
|
8785
|
+
const binaryPath = path9.join(binDir, runtimePostgresExecutableName(binary));
|
|
8546
8786
|
const result = postgresVersionProbe(binaryPath);
|
|
8547
8787
|
if (!/\bPostgreSQL\)?\s+18\.4\b/i.test(result)) {
|
|
8548
8788
|
throw new RuntimeInstallError(
|
|
@@ -8552,7 +8792,8 @@ async function validateRuntimePostgresVersion(cacheDir, binDir, postgresVersionP
|
|
|
8552
8792
|
}
|
|
8553
8793
|
}
|
|
8554
8794
|
}
|
|
8555
|
-
function extractRuntimePostgresArchive(archivePath, extractDir) {
|
|
8795
|
+
function extractRuntimePostgresArchive(archivePath, extractDir, cacheDir, deadline) {
|
|
8796
|
+
const timeout = remainingRuntimeInstallMs(deadline, cacheDir, "extract PostgreSQL runtime archive");
|
|
8556
8797
|
const result = process.platform === "win32" ? spawnSync2("powershell.exe", [
|
|
8557
8798
|
"-NoProfile",
|
|
8558
8799
|
"-NonInteractive",
|
|
@@ -8563,46 +8804,54 @@ function extractRuntimePostgresArchive(archivePath, extractDir) {
|
|
|
8563
8804
|
], {
|
|
8564
8805
|
encoding: "utf8",
|
|
8565
8806
|
env: { ...process.env, PG_ARCHIVE_PATH: archivePath, PG_EXTRACT_DIR: extractDir },
|
|
8566
|
-
windowsHide: true
|
|
8567
|
-
|
|
8807
|
+
windowsHide: true,
|
|
8808
|
+
...timeout === void 0 ? {} : { timeout }
|
|
8809
|
+
}) : spawnSync2("tar", ["-xf", archivePath, "-C", extractDir], {
|
|
8810
|
+
encoding: "utf8",
|
|
8811
|
+
...timeout === void 0 ? {} : { timeout }
|
|
8812
|
+
});
|
|
8568
8813
|
if (result.status !== 0) {
|
|
8569
8814
|
throw new Error(`failed to extract PostgreSQL archive: ${result.stderr || result.stdout}`);
|
|
8570
8815
|
}
|
|
8571
8816
|
}
|
|
8572
|
-
async function findRuntimePostgresBinDir(rootDir, cacheDir, postgresVersionProbe) {
|
|
8817
|
+
async function findRuntimePostgresBinDir(rootDir, cacheDir, postgresVersionProbe, deadline) {
|
|
8573
8818
|
const queue = [rootDir];
|
|
8574
8819
|
for (let index = 0; index < queue.length; index += 1) {
|
|
8575
8820
|
const current = queue[index];
|
|
8576
|
-
|
|
8821
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "find PostgreSQL runtime payload");
|
|
8822
|
+
if (await isRuntimePostgresPayloadUsable(cacheDir, current, postgresVersionProbe, deadline)) return current;
|
|
8577
8823
|
const entries = await readdir(current, { withFileTypes: true }).catch(() => []);
|
|
8578
8824
|
for (const entry of entries) {
|
|
8579
|
-
if (entry.isDirectory()) queue.push(
|
|
8825
|
+
if (entry.isDirectory()) queue.push(path9.join(current, entry.name));
|
|
8580
8826
|
}
|
|
8581
8827
|
}
|
|
8582
8828
|
return null;
|
|
8583
8829
|
}
|
|
8584
|
-
async function reconcileSharedPostgresPayloadGenerations(cacheDir, sharedPlatformRoot, postgresVersionProbe, cleanupDownloads = false) {
|
|
8585
|
-
|
|
8586
|
-
const
|
|
8830
|
+
async function reconcileSharedPostgresPayloadGenerations(cacheDir, sharedPlatformRoot, postgresVersionProbe, cleanupDownloads = false, deadline) {
|
|
8831
|
+
remainingRuntimeInstallMs(deadline, cacheDir, "reconcile shared PostgreSQL payload");
|
|
8832
|
+
const parentDir = path9.dirname(sharedPlatformRoot);
|
|
8833
|
+
const baseName = path9.basename(sharedPlatformRoot);
|
|
8587
8834
|
const entries = await readdir(parentDir, { withFileTypes: true }).catch(() => []);
|
|
8588
|
-
const temporaryRoots = entries.filter((entry) => entry.name.startsWith(`${baseName}.tmp-`)).map((entry) =>
|
|
8589
|
-
const previousRoots = entries.filter((entry) => entry.name.startsWith(`${baseName}.previous-`)).map((entry) =>
|
|
8590
|
-
const downloadsRoot =
|
|
8591
|
-
|
|
8835
|
+
const temporaryRoots = entries.filter((entry) => entry.name.startsWith(`${baseName}.tmp-`)).map((entry) => path9.join(parentDir, entry.name));
|
|
8836
|
+
const previousRoots = entries.filter((entry) => entry.name.startsWith(`${baseName}.previous-`)).map((entry) => path9.join(parentDir, entry.name)).sort().reverse();
|
|
8837
|
+
const downloadsRoot = path9.join(
|
|
8838
|
+
path9.dirname(path9.dirname(sharedPlatformRoot)),
|
|
8592
8839
|
".downloads"
|
|
8593
8840
|
);
|
|
8594
|
-
const staleDownloadRoots = cleanupDownloads ? (await readdir(downloadsRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.name.startsWith("postgres-18.4-")).map((entry) =>
|
|
8595
|
-
const canonicalBinDir =
|
|
8841
|
+
const staleDownloadRoots = cleanupDownloads ? (await readdir(downloadsRoot, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.name.startsWith("postgres-18.4-")).map((entry) => path9.join(downloadsRoot, entry.name)) : [];
|
|
8842
|
+
const canonicalBinDir = path9.join(sharedPlatformRoot, "bin");
|
|
8596
8843
|
if (!await isRuntimePostgresPayloadUsable(
|
|
8597
8844
|
cacheDir,
|
|
8598
8845
|
canonicalBinDir,
|
|
8599
|
-
postgresVersionProbe
|
|
8846
|
+
postgresVersionProbe,
|
|
8847
|
+
deadline
|
|
8600
8848
|
)) {
|
|
8601
8849
|
for (const previousRoot of previousRoots) {
|
|
8602
8850
|
if (!await isRuntimePostgresPayloadUsable(
|
|
8603
8851
|
cacheDir,
|
|
8604
|
-
|
|
8605
|
-
postgresVersionProbe
|
|
8852
|
+
path9.join(previousRoot, "bin"),
|
|
8853
|
+
postgresVersionProbe,
|
|
8854
|
+
deadline
|
|
8606
8855
|
)) {
|
|
8607
8856
|
continue;
|
|
8608
8857
|
}
|
|
@@ -8617,12 +8866,12 @@ async function reconcileSharedPostgresPayloadGenerations(cacheDir, sharedPlatfor
|
|
|
8617
8866
|
...staleDownloadRoots.map((candidate) => rm(candidate, { recursive: true, force: true }))
|
|
8618
8867
|
]);
|
|
8619
8868
|
}
|
|
8620
|
-
async function installSharedRuntimePostgresPayload(cacheDir, homeDir, sourceBinDir, postgresVersionProbe) {
|
|
8869
|
+
async function installSharedRuntimePostgresPayload(cacheDir, homeDir, sourceBinDir, postgresVersionProbe, deadline) {
|
|
8621
8870
|
const sharedBinDir = resolveSharedRuntimePostgresPayloadBinDir(homeDir);
|
|
8622
|
-
const sharedRuntimeDir =
|
|
8871
|
+
const sharedRuntimeDir = path9.dirname(sharedBinDir);
|
|
8623
8872
|
const sharedPlatformRoot = sharedRuntimeDir;
|
|
8624
|
-
const sourceRuntimeDir =
|
|
8625
|
-
const sourceTemplateDir = await resolveRuntimePostgresTemplateDir(sourceBinDir);
|
|
8873
|
+
const sourceRuntimeDir = path9.dirname(sourceBinDir);
|
|
8874
|
+
const sourceTemplateDir = await resolveRuntimePostgresTemplateDir(sourceBinDir, cacheDir, deadline);
|
|
8626
8875
|
if (!sourceTemplateDir) {
|
|
8627
8876
|
throw new RuntimeInstallError(
|
|
8628
8877
|
`${RUDDER_POSTGRES_BIN_DIR_ENV} must contain PostgreSQL 18.4 initdb template files`,
|
|
@@ -8634,13 +8883,15 @@ async function installSharedRuntimePostgresPayload(cacheDir, homeDir, sourceBinD
|
|
|
8634
8883
|
await reconcileSharedPostgresPayloadGenerations(
|
|
8635
8884
|
cacheDir,
|
|
8636
8885
|
sharedPlatformRoot,
|
|
8637
|
-
postgresVersionProbe
|
|
8886
|
+
postgresVersionProbe,
|
|
8887
|
+
false,
|
|
8888
|
+
deadline
|
|
8638
8889
|
);
|
|
8639
|
-
if (await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe)) {
|
|
8890
|
+
if (await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe, deadline)) {
|
|
8640
8891
|
return sharedBinDir;
|
|
8641
8892
|
}
|
|
8642
8893
|
await assertSharedPostgresPayloadNotLive(cacheDir, homeDir, sharedBinDir);
|
|
8643
|
-
await
|
|
8894
|
+
await mkdir(path9.dirname(sharedPlatformRoot), { recursive: true });
|
|
8644
8895
|
const temporaryPlatformRoot = `${sharedPlatformRoot}.tmp-${process.pid}-${Date.now()}`;
|
|
8645
8896
|
const previousPlatformRoot = `${sharedPlatformRoot}.previous-${process.pid}-${Date.now()}`;
|
|
8646
8897
|
let previousMoved = false;
|
|
@@ -8650,13 +8901,15 @@ async function installSharedRuntimePostgresPayload(cacheDir, homeDir, sourceBinD
|
|
|
8650
8901
|
try {
|
|
8651
8902
|
const temporaryRuntimeDir = temporaryPlatformRoot;
|
|
8652
8903
|
const sourceShareDir = resolveRuntimePostgresShareDir(sourceBinDir, sourceTemplateDir);
|
|
8653
|
-
await
|
|
8904
|
+
await copyRuntimePostgresPayloadWithinDeadline(
|
|
8654
8905
|
sourceRuntimeDir,
|
|
8655
8906
|
temporaryRuntimeDir,
|
|
8656
|
-
sourceShareDir
|
|
8907
|
+
sourceShareDir,
|
|
8908
|
+
cacheDir,
|
|
8909
|
+
deadline
|
|
8657
8910
|
);
|
|
8658
|
-
const temporaryBinDir =
|
|
8659
|
-
await validateRuntimePostgresVersion(cacheDir, temporaryBinDir, postgresVersionProbe);
|
|
8911
|
+
const temporaryBinDir = path9.join(temporaryRuntimeDir, "bin");
|
|
8912
|
+
await validateRuntimePostgresVersion(cacheDir, temporaryBinDir, postgresVersionProbe, deadline);
|
|
8660
8913
|
try {
|
|
8661
8914
|
await rename(sharedPlatformRoot, previousPlatformRoot);
|
|
8662
8915
|
previousMoved = true;
|
|
@@ -8684,30 +8937,35 @@ async function installSharedRuntimePostgresPayload(cacheDir, homeDir, sourceBinD
|
|
|
8684
8937
|
}
|
|
8685
8938
|
}
|
|
8686
8939
|
return sharedBinDir;
|
|
8687
|
-
});
|
|
8940
|
+
}, { deadline, cacheDir, command: "acquire shared PostgreSQL install lock" });
|
|
8688
8941
|
}
|
|
8689
|
-
async function downloadSharedRuntimePostgresPayload(cacheDir, homeDir, postgresVersionProbe) {
|
|
8942
|
+
async function downloadSharedRuntimePostgresPayload(cacheDir, homeDir, postgresVersionProbe, deadline, cleanupWorkDir) {
|
|
8690
8943
|
const archiveSource = resolvePostgresRuntimeArchiveSource();
|
|
8691
8944
|
const archiveUrl = archiveSource?.url ?? null;
|
|
8692
8945
|
if (!archiveUrl) return null;
|
|
8693
8946
|
const sharedBinDir = resolveSharedRuntimePostgresPayloadBinDir(homeDir);
|
|
8694
|
-
const sharedPlatformRoot =
|
|
8947
|
+
const sharedPlatformRoot = path9.dirname(sharedBinDir);
|
|
8695
8948
|
const downloadLockPath = `${sharedPlatformRoot}.download.lock`;
|
|
8696
8949
|
return withRuntimeFilesystemLock(downloadLockPath, async () => {
|
|
8697
|
-
if (await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe)) {
|
|
8950
|
+
if (await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe, deadline)) {
|
|
8698
8951
|
return sharedBinDir;
|
|
8699
8952
|
}
|
|
8700
|
-
const workRoot =
|
|
8701
|
-
await
|
|
8702
|
-
const workDir = await mkdtemp(
|
|
8703
|
-
const archivePath =
|
|
8704
|
-
const extractDir =
|
|
8953
|
+
const workRoot = path9.join(homeDir, "runtime-payloads", ".downloads");
|
|
8954
|
+
await mkdir(workRoot, { recursive: true });
|
|
8955
|
+
const workDir = await mkdtemp(path9.join(workRoot, "postgres-18.4-"));
|
|
8956
|
+
const archivePath = path9.join(workDir, "postgresql-18.4.zip");
|
|
8957
|
+
const extractDir = path9.join(workDir, "extract");
|
|
8705
8958
|
try {
|
|
8706
|
-
await downloadRuntimePostgresArchive(
|
|
8959
|
+
await downloadRuntimePostgresArchive(
|
|
8960
|
+
archiveUrl,
|
|
8961
|
+
archivePath,
|
|
8962
|
+
archiveSource?.expectedSha256,
|
|
8963
|
+
{ timeoutMs: remainingRuntimeInstallMs(deadline, cacheDir, "download PostgreSQL runtime archive") }
|
|
8964
|
+
);
|
|
8707
8965
|
const configuredMaxBytes = Number.parseInt(process.env[RUDDER_POSTGRES_RUNTIME_ARCHIVE_MAX_BYTES_ENV2] ?? "", 10);
|
|
8708
8966
|
const maxArchiveBytes = Number.isSafeInteger(configuredMaxBytes) && configuredMaxBytes > 0 ? configuredMaxBytes : DEFAULT_RUNTIME_POSTGRES_ARCHIVE_MAX_BYTES2;
|
|
8709
8967
|
const nativePublishStaging = `${sharedPlatformRoot}.tmp-native-${process.pid}-${Date.now()}`;
|
|
8710
|
-
await
|
|
8968
|
+
await mkdir(path9.dirname(sharedPlatformRoot), { recursive: true });
|
|
8711
8969
|
const nativeInstall = await tryInstallNativePayload({
|
|
8712
8970
|
archivePath,
|
|
8713
8971
|
extractPath: extractDir,
|
|
@@ -8715,11 +8973,14 @@ async function downloadSharedRuntimePostgresPayload(cacheDir, homeDir, postgresV
|
|
|
8715
8973
|
destinationPath: sharedPlatformRoot,
|
|
8716
8974
|
maxArchiveBytes,
|
|
8717
8975
|
expectedSha256: archiveSource?.expectedSha256,
|
|
8718
|
-
|
|
8976
|
+
timeoutMs: remainingRuntimeInstallMs(deadline, cacheDir, "prepare native PostgreSQL runtime payload"),
|
|
8977
|
+
now: deadline?.now,
|
|
8978
|
+
preparePublish: async (nativeExtractPath, publishStagingPath, context) => {
|
|
8719
8979
|
const extractedBinDir2 = await findRuntimePostgresBinDir(
|
|
8720
8980
|
nativeExtractPath,
|
|
8721
8981
|
cacheDir,
|
|
8722
|
-
postgresVersionProbe
|
|
8982
|
+
postgresVersionProbe,
|
|
8983
|
+
deadline
|
|
8723
8984
|
);
|
|
8724
8985
|
if (!extractedBinDir2) {
|
|
8725
8986
|
throw new RuntimeInstallError(
|
|
@@ -8727,41 +8988,46 @@ async function downloadSharedRuntimePostgresPayload(cacheDir, homeDir, postgresV
|
|
|
8727
8988
|
{ cacheDir, command: "prepare native PostgreSQL runtime payload", output: "" }
|
|
8728
8989
|
);
|
|
8729
8990
|
}
|
|
8730
|
-
await validateRuntimePostgresVersion(cacheDir, extractedBinDir2, postgresVersionProbe);
|
|
8731
|
-
const templateDir = await resolveRuntimePostgresTemplateDir(extractedBinDir2);
|
|
8991
|
+
await validateRuntimePostgresVersion(cacheDir, extractedBinDir2, postgresVersionProbe, deadline);
|
|
8992
|
+
const templateDir = await resolveRuntimePostgresTemplateDir(extractedBinDir2, cacheDir, deadline);
|
|
8732
8993
|
if (!templateDir) {
|
|
8733
8994
|
throw new RuntimeInstallError(
|
|
8734
8995
|
"PostgreSQL 18.4 archive did not contain initdb template files",
|
|
8735
8996
|
{ cacheDir, command: "prepare native PostgreSQL runtime payload", output: "" }
|
|
8736
8997
|
);
|
|
8737
8998
|
}
|
|
8738
|
-
await
|
|
8739
|
-
|
|
8999
|
+
await copyRuntimePostgresPayloadWithinDeadline(
|
|
9000
|
+
path9.dirname(extractedBinDir2),
|
|
8740
9001
|
publishStagingPath,
|
|
8741
|
-
resolveRuntimePostgresShareDir(extractedBinDir2, templateDir)
|
|
9002
|
+
resolveRuntimePostgresShareDir(extractedBinDir2, templateDir),
|
|
9003
|
+
cacheDir,
|
|
9004
|
+
deadline,
|
|
9005
|
+
context.signal
|
|
8742
9006
|
);
|
|
8743
|
-
return
|
|
9007
|
+
return path9.relative(
|
|
8744
9008
|
publishStagingPath,
|
|
8745
|
-
|
|
9009
|
+
path9.join(publishStagingPath, "bin", runtimePostgresExecutableName("postgres"))
|
|
8746
9010
|
);
|
|
8747
9011
|
},
|
|
8748
9012
|
validatePublished: async (destinationPath) => {
|
|
8749
9013
|
await validateRuntimePostgresVersion(
|
|
8750
9014
|
cacheDir,
|
|
8751
|
-
|
|
8752
|
-
postgresVersionProbe
|
|
9015
|
+
path9.join(destinationPath, "bin"),
|
|
9016
|
+
postgresVersionProbe,
|
|
9017
|
+
deadline
|
|
8753
9018
|
);
|
|
8754
9019
|
}
|
|
8755
9020
|
});
|
|
8756
9021
|
if (nativeInstall.installed) {
|
|
8757
9022
|
return sharedBinDir;
|
|
8758
9023
|
}
|
|
8759
|
-
await
|
|
8760
|
-
extractRuntimePostgresArchive(archivePath, extractDir);
|
|
9024
|
+
await mkdir(extractDir, { recursive: true });
|
|
9025
|
+
extractRuntimePostgresArchive(archivePath, extractDir, cacheDir, deadline);
|
|
8761
9026
|
const extractedBinDir = await findRuntimePostgresBinDir(
|
|
8762
9027
|
extractDir,
|
|
8763
9028
|
cacheDir,
|
|
8764
|
-
postgresVersionProbe
|
|
9029
|
+
postgresVersionProbe,
|
|
9030
|
+
deadline
|
|
8765
9031
|
);
|
|
8766
9032
|
if (!extractedBinDir) {
|
|
8767
9033
|
throw new RuntimeInstallError(
|
|
@@ -8773,28 +9039,31 @@ async function downloadSharedRuntimePostgresPayload(cacheDir, homeDir, postgresV
|
|
|
8773
9039
|
cacheDir,
|
|
8774
9040
|
homeDir,
|
|
8775
9041
|
extractedBinDir,
|
|
8776
|
-
postgresVersionProbe
|
|
9042
|
+
postgresVersionProbe,
|
|
9043
|
+
deadline
|
|
8777
9044
|
);
|
|
8778
9045
|
} finally {
|
|
8779
|
-
|
|
9046
|
+
const cleanup = cleanupWorkDir ?? ((candidate) => rm(candidate, { recursive: true, force: true }));
|
|
9047
|
+
void cleanup(workDir).catch(() => {
|
|
9048
|
+
});
|
|
8780
9049
|
}
|
|
8781
|
-
});
|
|
9050
|
+
}, { deadline, cacheDir, command: "acquire shared PostgreSQL download lock" });
|
|
8782
9051
|
}
|
|
8783
9052
|
async function ensureRuntimePostgresCompatibilityLink(cacheDir, homeDir, packageVersion) {
|
|
8784
|
-
const compatibilityRoot =
|
|
8785
|
-
const sharedPayloadRoot =
|
|
9053
|
+
const compatibilityRoot = path9.join(cacheDir, RUNTIME_POSTGRES_PAYLOAD_DIR);
|
|
9054
|
+
const sharedPayloadRoot = path9.join(homeDir, "runtime-payloads", RUNTIME_POSTGRES_PAYLOAD_DIR);
|
|
8786
9055
|
const runtimeMetadata = await readRuntimeInstallMetadata(cacheDir);
|
|
8787
9056
|
const liveDescriptors = await readLiveRuntimeDescriptors(homeDir);
|
|
8788
9057
|
const compatibilityBinDir = resolveRuntimePostgresPayloadBinDir(cacheDir);
|
|
8789
9058
|
const isProtected = liveDescriptors.some((descriptor) => descriptor.postgresBinDir && pathIsInside(descriptor.postgresBinDir, compatibilityRoot) || !descriptor.postgresBinDir && descriptor.version === (runtimeMetadata?.packageVersion ?? packageVersion));
|
|
8790
9059
|
if (isProtected) return;
|
|
8791
|
-
await
|
|
9060
|
+
await mkdir(path9.dirname(compatibilityRoot), { recursive: true });
|
|
8792
9061
|
const temporaryRoot = `${compatibilityRoot}.next-${process.pid}-${Date.now()}`;
|
|
8793
9062
|
const previousRoot = `${compatibilityRoot}.previous-${process.pid}-${Date.now()}`;
|
|
8794
9063
|
await rm(temporaryRoot, { recursive: true, force: true });
|
|
8795
9064
|
await rm(previousRoot, { recursive: true, force: true });
|
|
8796
9065
|
await symlink(
|
|
8797
|
-
process.platform === "win32" ? sharedPayloadRoot :
|
|
9066
|
+
process.platform === "win32" ? sharedPayloadRoot : path9.relative(path9.dirname(compatibilityRoot), sharedPayloadRoot),
|
|
8798
9067
|
temporaryRoot,
|
|
8799
9068
|
process.platform === "win32" ? "junction" : "dir"
|
|
8800
9069
|
);
|
|
@@ -8824,15 +9093,16 @@ async function ensureRuntimePostgresCompatibilityLink(cacheDir, homeDir, package
|
|
|
8824
9093
|
if (!previousMoved) await rm(previousRoot, { recursive: true, force: true });
|
|
8825
9094
|
}
|
|
8826
9095
|
}
|
|
8827
|
-
async function stageRuntimePostgresPayload(cacheDir, homeDir, packageVersion, enabled, postgresVersionProbe) {
|
|
9096
|
+
async function stageRuntimePostgresPayload(cacheDir, homeDir, packageVersion, enabled, postgresVersionProbe, deadline, cleanupDownloadWorkDir) {
|
|
8828
9097
|
if (!enabled) return { output: "" };
|
|
8829
9098
|
const explicitSourceBinDir = process.env[RUDDER_POSTGRES_BIN_DIR_ENV]?.trim();
|
|
8830
|
-
const resolvedExplicitSourceBinDir = explicitSourceBinDir ?
|
|
9099
|
+
const resolvedExplicitSourceBinDir = explicitSourceBinDir ? path9.resolve(explicitSourceBinDir) : null;
|
|
8831
9100
|
if (resolvedExplicitSourceBinDir && !isManagedRuntimePostgresBinDir(resolvedExplicitSourceBinDir, homeDir)) {
|
|
8832
9101
|
await validateRuntimePostgresVersion(
|
|
8833
9102
|
cacheDir,
|
|
8834
9103
|
resolvedExplicitSourceBinDir,
|
|
8835
|
-
postgresVersionProbe
|
|
9104
|
+
postgresVersionProbe,
|
|
9105
|
+
deadline
|
|
8836
9106
|
);
|
|
8837
9107
|
return {
|
|
8838
9108
|
output: "",
|
|
@@ -8847,35 +9117,41 @@ async function stageRuntimePostgresPayload(cacheDir, homeDir, packageVersion, en
|
|
|
8847
9117
|
};
|
|
8848
9118
|
}
|
|
8849
9119
|
const sharedBinDir = resolveSharedRuntimePostgresPayloadBinDir(homeDir);
|
|
8850
|
-
const sharedPlatformRoot =
|
|
9120
|
+
const sharedPlatformRoot = path9.dirname(sharedBinDir);
|
|
8851
9121
|
await withRuntimeFilesystemLock(
|
|
8852
9122
|
`${sharedPlatformRoot}.install.lock`,
|
|
8853
9123
|
async () => reconcileSharedPostgresPayloadGenerations(
|
|
8854
9124
|
cacheDir,
|
|
8855
9125
|
sharedPlatformRoot,
|
|
8856
9126
|
postgresVersionProbe,
|
|
8857
|
-
true
|
|
8858
|
-
|
|
9127
|
+
true,
|
|
9128
|
+
deadline
|
|
9129
|
+
),
|
|
9130
|
+
{ deadline, cacheDir, command: "reconcile shared PostgreSQL payload" }
|
|
8859
9131
|
);
|
|
8860
9132
|
let output = "";
|
|
8861
|
-
if (!await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe)) {
|
|
8862
|
-
const sourceBinDir = resolvedExplicitSourceBinDir ?? await findLegacyRuntimePostgresBinDir(cacheDir, homeDir, postgresVersionProbe);
|
|
9133
|
+
if (!await isRuntimePostgresPayloadUsable(cacheDir, sharedBinDir, postgresVersionProbe, deadline)) {
|
|
9134
|
+
const sourceBinDir = resolvedExplicitSourceBinDir ?? await findLegacyRuntimePostgresBinDir(cacheDir, homeDir, postgresVersionProbe, deadline);
|
|
8863
9135
|
if (sourceBinDir) {
|
|
8864
9136
|
await validateRuntimePostgresVersion(
|
|
8865
9137
|
cacheDir,
|
|
8866
9138
|
sourceBinDir,
|
|
8867
|
-
postgresVersionProbe
|
|
9139
|
+
postgresVersionProbe,
|
|
9140
|
+
deadline
|
|
8868
9141
|
);
|
|
8869
9142
|
await installSharedRuntimePostgresPayload(
|
|
8870
9143
|
cacheDir,
|
|
8871
9144
|
homeDir,
|
|
8872
9145
|
sourceBinDir,
|
|
8873
|
-
postgresVersionProbe
|
|
9146
|
+
postgresVersionProbe,
|
|
9147
|
+
deadline
|
|
8874
9148
|
);
|
|
8875
9149
|
} else if (!await downloadSharedRuntimePostgresPayload(
|
|
8876
9150
|
cacheDir,
|
|
8877
9151
|
homeDir,
|
|
8878
|
-
postgresVersionProbe
|
|
9152
|
+
postgresVersionProbe,
|
|
9153
|
+
deadline,
|
|
9154
|
+
cleanupDownloadWorkDir
|
|
8879
9155
|
)) {
|
|
8880
9156
|
return { output: "" };
|
|
8881
9157
|
}
|
|
@@ -8953,13 +9229,13 @@ async function pruneRuntimeCache(options = {}) {
|
|
|
8953
9229
|
};
|
|
8954
9230
|
}
|
|
8955
9231
|
async function scanRuntimeCacheEntries(homeDir) {
|
|
8956
|
-
const runtimesDir =
|
|
9232
|
+
const runtimesDir = path9.join(homeDir, "runtimes");
|
|
8957
9233
|
const dirents = await readdir(runtimesDir, { withFileTypes: true }).catch(() => null);
|
|
8958
9234
|
if (!dirents) return [];
|
|
8959
9235
|
const entries = [];
|
|
8960
9236
|
for (const dirent of dirents) {
|
|
8961
9237
|
if (!dirent.isDirectory()) continue;
|
|
8962
|
-
const cacheDir =
|
|
9238
|
+
const cacheDir = path9.join(runtimesDir, dirent.name);
|
|
8963
9239
|
const metadata = await readRuntimeInstallMetadata(cacheDir);
|
|
8964
9240
|
if (!metadata) continue;
|
|
8965
9241
|
const fallbackStat = await safeStat(cacheDir);
|
|
@@ -8982,7 +9258,7 @@ function parseTimestampMs(value) {
|
|
|
8982
9258
|
}
|
|
8983
9259
|
async function safeStat(targetPath) {
|
|
8984
9260
|
try {
|
|
8985
|
-
return await
|
|
9261
|
+
return await stat(targetPath);
|
|
8986
9262
|
} catch {
|
|
8987
9263
|
return null;
|
|
8988
9264
|
}
|
|
@@ -8992,7 +9268,7 @@ async function directorySizeBytes(targetPath) {
|
|
|
8992
9268
|
if (!dirents) return 0;
|
|
8993
9269
|
let total = 0;
|
|
8994
9270
|
for (const dirent of dirents) {
|
|
8995
|
-
const entryPath =
|
|
9271
|
+
const entryPath = path9.join(targetPath, dirent.name);
|
|
8996
9272
|
if (dirent.isSymbolicLink()) continue;
|
|
8997
9273
|
if (dirent.isDirectory()) {
|
|
8998
9274
|
total += await directorySizeBytes(entryPath);
|
|
@@ -9004,14 +9280,14 @@ async function directorySizeBytes(targetPath) {
|
|
|
9004
9280
|
return total;
|
|
9005
9281
|
}
|
|
9006
9282
|
async function readActiveRuntimeVersions(homeDir) {
|
|
9007
|
-
const instancesDir =
|
|
9283
|
+
const instancesDir = path9.join(homeDir, "instances");
|
|
9008
9284
|
const dirents = await readdir(instancesDir, { withFileTypes: true }).catch(() => null);
|
|
9009
9285
|
if (!dirents) return [];
|
|
9010
9286
|
const versions = /* @__PURE__ */ new Set();
|
|
9011
9287
|
for (const dirent of dirents) {
|
|
9012
9288
|
if (!dirent.isDirectory()) continue;
|
|
9013
9289
|
try {
|
|
9014
|
-
const descriptorPath =
|
|
9290
|
+
const descriptorPath = path9.join(instancesDir, dirent.name, "runtime", "server.json");
|
|
9015
9291
|
const parsed = JSON.parse(await readFile(descriptorPath, "utf8"));
|
|
9016
9292
|
if (typeof parsed.version !== "string") continue;
|
|
9017
9293
|
if (typeof parsed.pid === "number" && Number.isInteger(parsed.pid) && parsed.pid > 0 && isPidRunning(parsed.pid)) {
|
|
@@ -9126,7 +9402,6 @@ var init_install = __esm({
|
|
|
9126
9402
|
"use strict";
|
|
9127
9403
|
init_home();
|
|
9128
9404
|
init_native_payload();
|
|
9129
|
-
init_postgres_payload();
|
|
9130
9405
|
init_postgres_runtime_download();
|
|
9131
9406
|
init_postgres_runtime_source();
|
|
9132
9407
|
RUNTIME_NPM_PACKAGE_NAME = "@rudderhq/server";
|
|
@@ -9172,7 +9447,7 @@ var init_install = __esm({
|
|
|
9172
9447
|
|
|
9173
9448
|
// src/runtime/server-entry.ts
|
|
9174
9449
|
import fs7 from "node:fs";
|
|
9175
|
-
import
|
|
9450
|
+
import path10 from "node:path";
|
|
9176
9451
|
import { fileURLToPath as fileURLToPath5, pathToFileURL as pathToFileURL2 } from "node:url";
|
|
9177
9452
|
function formatError(err) {
|
|
9178
9453
|
if (err instanceof Error) {
|
|
@@ -9194,8 +9469,8 @@ function maybeEnableUiDevMiddleware(entrypoint) {
|
|
|
9194
9469
|
}
|
|
9195
9470
|
}
|
|
9196
9471
|
function resolveDevServerEntry() {
|
|
9197
|
-
const projectRoot =
|
|
9198
|
-
return
|
|
9472
|
+
const projectRoot = path10.resolve(path10.dirname(fileURLToPath5(import.meta.url)), "../../..");
|
|
9473
|
+
return path10.resolve(projectRoot, "server/src/index.ts");
|
|
9199
9474
|
}
|
|
9200
9475
|
async function loadServerRuntimeModule(options) {
|
|
9201
9476
|
const devEntry = resolveDevServerEntry();
|
|
@@ -9335,21 +9610,21 @@ var init_auth_bootstrap_ceo = __esm({
|
|
|
9335
9610
|
|
|
9336
9611
|
// src/utils/path-resolver.ts
|
|
9337
9612
|
import fs10 from "node:fs";
|
|
9338
|
-
import
|
|
9613
|
+
import path18 from "node:path";
|
|
9339
9614
|
function unique(items) {
|
|
9340
9615
|
return Array.from(new Set(items));
|
|
9341
9616
|
}
|
|
9342
9617
|
function resolveRuntimeLikePath(value, configPath) {
|
|
9343
9618
|
const expanded = expandHomePrefix(value);
|
|
9344
|
-
if (
|
|
9619
|
+
if (path18.isAbsolute(expanded)) return path18.resolve(expanded);
|
|
9345
9620
|
const cwd = process.cwd();
|
|
9346
|
-
const configDir = configPath ?
|
|
9347
|
-
const workspaceRoot = configDir ?
|
|
9621
|
+
const configDir = configPath ? path18.dirname(configPath) : null;
|
|
9622
|
+
const workspaceRoot = configDir ? path18.resolve(configDir, "..") : cwd;
|
|
9348
9623
|
const candidates = unique([
|
|
9349
|
-
...configDir ? [
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9624
|
+
...configDir ? [path18.resolve(configDir, expanded)] : [],
|
|
9625
|
+
path18.resolve(workspaceRoot, "server", expanded),
|
|
9626
|
+
path18.resolve(workspaceRoot, expanded),
|
|
9627
|
+
path18.resolve(cwd, expanded)
|
|
9353
9628
|
]);
|
|
9354
9629
|
return candidates.find((candidate) => fs10.existsSync(candidate)) ?? candidates[0];
|
|
9355
9630
|
}
|
|
@@ -9363,7 +9638,7 @@ var init_path_resolver = __esm({
|
|
|
9363
9638
|
// src/config/secrets-key.ts
|
|
9364
9639
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
9365
9640
|
import fs11 from "node:fs";
|
|
9366
|
-
import
|
|
9641
|
+
import path19 from "node:path";
|
|
9367
9642
|
function ensureLocalSecretsKeyFile(config, configPath) {
|
|
9368
9643
|
if (config.secrets.provider !== "local_encrypted") {
|
|
9369
9644
|
return { status: "skipped_provider", path: null };
|
|
@@ -9378,7 +9653,7 @@ function ensureLocalSecretsKeyFile(config, configPath) {
|
|
|
9378
9653
|
if (fs11.existsSync(keyFilePath)) {
|
|
9379
9654
|
return { status: "existing", path: keyFilePath };
|
|
9380
9655
|
}
|
|
9381
|
-
fs11.mkdirSync(
|
|
9656
|
+
fs11.mkdirSync(path19.dirname(keyFilePath), { recursive: true });
|
|
9382
9657
|
fs11.writeFileSync(keyFilePath, randomBytes2(32).toString("base64"), {
|
|
9383
9658
|
encoding: "utf8",
|
|
9384
9659
|
mode: 384
|
|
@@ -10464,7 +10739,7 @@ var init_port_check = __esm({
|
|
|
10464
10739
|
// src/checks/secrets-check.ts
|
|
10465
10740
|
import { randomBytes as randomBytes3 } from "node:crypto";
|
|
10466
10741
|
import fs14 from "node:fs";
|
|
10467
|
-
import
|
|
10742
|
+
import path20 from "node:path";
|
|
10468
10743
|
function decodeMasterKey(raw) {
|
|
10469
10744
|
const trimmed = raw.trim();
|
|
10470
10745
|
if (!trimmed) return null;
|
|
@@ -10534,7 +10809,7 @@ function secretsCheck(config, configPath) {
|
|
|
10534
10809
|
message: `Secrets key file does not exist yet: ${keyFilePath}`,
|
|
10535
10810
|
canRepair: true,
|
|
10536
10811
|
repair: () => {
|
|
10537
|
-
fs14.mkdirSync(
|
|
10812
|
+
fs14.mkdirSync(path20.dirname(keyFilePath), { recursive: true });
|
|
10538
10813
|
fs14.writeFileSync(keyFilePath, randomBytes3(32).toString("base64"), {
|
|
10539
10814
|
encoding: "utf8",
|
|
10540
10815
|
mode: 384
|
|
@@ -11107,7 +11382,7 @@ var init_run = __esm({
|
|
|
11107
11382
|
|
|
11108
11383
|
// src/commands/onboard.ts
|
|
11109
11384
|
import * as p14 from "@clack/prompts";
|
|
11110
|
-
import
|
|
11385
|
+
import path21 from "node:path";
|
|
11111
11386
|
import pc13 from "picocolors";
|
|
11112
11387
|
function parseBooleanFromEnv(rawValue) {
|
|
11113
11388
|
if (rawValue === void 0) return null;
|
|
@@ -11140,7 +11415,7 @@ function parseEnumFromEnv(rawValue, allowedValues) {
|
|
|
11140
11415
|
}
|
|
11141
11416
|
function resolvePathFromEnv(rawValue) {
|
|
11142
11417
|
if (!rawValue || rawValue.trim().length === 0) return null;
|
|
11143
|
-
return
|
|
11418
|
+
return path21.resolve(expandHomePrefix(rawValue.trim()));
|
|
11144
11419
|
}
|
|
11145
11420
|
function quickstartDefaultsFromEnv() {
|
|
11146
11421
|
const instanceId = resolveRudderInstanceId();
|
|
@@ -11572,7 +11847,7 @@ var init_onboard = __esm({
|
|
|
11572
11847
|
});
|
|
11573
11848
|
|
|
11574
11849
|
// src/program.ts
|
|
11575
|
-
import { Command, CommanderError } from "commander";
|
|
11850
|
+
import { Command, CommanderError, Option } from "commander";
|
|
11576
11851
|
|
|
11577
11852
|
// src/agent-v1-mcp-server.ts
|
|
11578
11853
|
init_dist();
|
|
@@ -13105,41 +13380,41 @@ var RudderApiClient = class {
|
|
|
13105
13380
|
this.signal = opts.signal;
|
|
13106
13381
|
this.recoverAuth = opts.recoverAuth;
|
|
13107
13382
|
}
|
|
13108
|
-
get(
|
|
13109
|
-
return this.request(
|
|
13383
|
+
get(path25, opts) {
|
|
13384
|
+
return this.request(path25, { method: "GET" }, opts);
|
|
13110
13385
|
}
|
|
13111
|
-
post(
|
|
13112
|
-
return this.request(
|
|
13386
|
+
post(path25, body, opts) {
|
|
13387
|
+
return this.request(path25, {
|
|
13113
13388
|
method: "POST",
|
|
13114
13389
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
13115
13390
|
}, opts);
|
|
13116
13391
|
}
|
|
13117
|
-
postForm(
|
|
13118
|
-
return this.request(
|
|
13392
|
+
postForm(path25, form, opts) {
|
|
13393
|
+
return this.request(path25, {
|
|
13119
13394
|
method: "POST",
|
|
13120
13395
|
body: form
|
|
13121
13396
|
}, opts);
|
|
13122
13397
|
}
|
|
13123
|
-
patch(
|
|
13124
|
-
return this.request(
|
|
13398
|
+
patch(path25, body, opts) {
|
|
13399
|
+
return this.request(path25, {
|
|
13125
13400
|
method: "PATCH",
|
|
13126
13401
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
13127
13402
|
}, opts);
|
|
13128
13403
|
}
|
|
13129
|
-
put(
|
|
13130
|
-
return this.request(
|
|
13404
|
+
put(path25, body, opts) {
|
|
13405
|
+
return this.request(path25, {
|
|
13131
13406
|
method: "PUT",
|
|
13132
13407
|
body: body === void 0 ? void 0 : JSON.stringify(body)
|
|
13133
13408
|
}, opts);
|
|
13134
13409
|
}
|
|
13135
|
-
delete(
|
|
13136
|
-
return this.request(
|
|
13410
|
+
delete(path25, opts) {
|
|
13411
|
+
return this.request(path25, { method: "DELETE" }, opts);
|
|
13137
13412
|
}
|
|
13138
13413
|
setApiKey(apiKey) {
|
|
13139
13414
|
this.apiKey = apiKey?.trim() || void 0;
|
|
13140
13415
|
}
|
|
13141
|
-
async request(
|
|
13142
|
-
const url = buildUrl(this.apiBase,
|
|
13416
|
+
async request(path25, init, opts, hasRetriedAuth = false) {
|
|
13417
|
+
const url = buildUrl(this.apiBase, path25);
|
|
13143
13418
|
const headers = {
|
|
13144
13419
|
accept: "application/json",
|
|
13145
13420
|
...toStringRecord(init.headers)
|
|
@@ -13170,13 +13445,13 @@ var RudderApiClient = class {
|
|
|
13170
13445
|
const apiError = await toApiError(response);
|
|
13171
13446
|
if (!hasRetriedAuth && this.recoverAuth) {
|
|
13172
13447
|
const recoveredToken = await this.recoverAuth({
|
|
13173
|
-
path:
|
|
13448
|
+
path: path25,
|
|
13174
13449
|
method: String(init.method ?? "GET").toUpperCase(),
|
|
13175
13450
|
error: apiError
|
|
13176
13451
|
});
|
|
13177
13452
|
if (recoveredToken) {
|
|
13178
13453
|
this.setApiKey(recoveredToken);
|
|
13179
|
-
return this.request(
|
|
13454
|
+
return this.request(path25, init, opts, true);
|
|
13180
13455
|
}
|
|
13181
13456
|
}
|
|
13182
13457
|
throw apiError;
|
|
@@ -13195,8 +13470,8 @@ function shouldAttachAgentContext(method) {
|
|
|
13195
13470
|
const normalized = String(method ?? "GET").toUpperCase();
|
|
13196
13471
|
return normalized !== "GET" && normalized !== "HEAD";
|
|
13197
13472
|
}
|
|
13198
|
-
function buildUrl(apiBase,
|
|
13199
|
-
const normalizedPath =
|
|
13473
|
+
function buildUrl(apiBase, path25) {
|
|
13474
|
+
const normalizedPath = path25.startsWith("/") ? path25 : `/${path25}`;
|
|
13200
13475
|
const [pathname, query] = normalizedPath.split("?");
|
|
13201
13476
|
const url = new URL2(apiBase);
|
|
13202
13477
|
url.pathname = `${url.pathname.replace(/\/+$/, "")}${pathname}`;
|
|
@@ -14359,7 +14634,7 @@ async function callToolDirectlyIfSupported(toolName, rawArgs, env, signal) {
|
|
|
14359
14634
|
if (hasLocalImageInputs(input.images)) return null;
|
|
14360
14635
|
const api = mcpApiClient(env, signal);
|
|
14361
14636
|
const success = (data) => mcpSuccess(
|
|
14362
|
-
toCliShortIdOutput(data),
|
|
14637
|
+
capabilityId.startsWith("browser.") ? data : toCliShortIdOutput(data),
|
|
14363
14638
|
capabilityId.startsWith("browser.") ? RUDDER_BROWSER_MCP_MAX_TOOL_RESULT_BYTES : RUDDER_MCP_MAX_TOOL_RESULT_BYTES
|
|
14364
14639
|
);
|
|
14365
14640
|
switch (capabilityId) {
|
|
@@ -15786,8 +16061,8 @@ function registerActivityCommands(program) {
|
|
|
15786
16061
|
if (opts.entityType) params.set("entityType", opts.entityType);
|
|
15787
16062
|
if (opts.entityId) params.set("entityId", opts.entityId);
|
|
15788
16063
|
const query = params.toString();
|
|
15789
|
-
const
|
|
15790
|
-
const rows = await ctx.api.get(
|
|
16064
|
+
const path25 = `/api/orgs/${ctx.orgId}/activity${query ? `?${query}` : ""}`;
|
|
16065
|
+
const rows = await ctx.api.get(path25) ?? [];
|
|
15791
16066
|
if (ctx.json) {
|
|
15792
16067
|
printOutput(rows, { json: true });
|
|
15793
16068
|
return;
|
|
@@ -15819,7 +16094,7 @@ function registerActivityCommands(program) {
|
|
|
15819
16094
|
|
|
15820
16095
|
// ../packages/agent-runtime-utils/dist/server-utils.cli.js
|
|
15821
16096
|
import { promises as fs8 } from "node:fs";
|
|
15822
|
-
import
|
|
16097
|
+
import path11 from "node:path";
|
|
15823
16098
|
|
|
15824
16099
|
// ../packages/agent-runtime-utils/dist/native-process-runner.js
|
|
15825
16100
|
init_dist2();
|
|
@@ -16093,12 +16368,11 @@ The prior Run has already persisted the checkpoint named above. Reconstruct the
|
|
|
16093
16368
|
|
|
16094
16369
|
If the checkpoint instead records a wait or human decision, this wake is unexpected: do not invent authorization or execute the waiting action. Refresh managed Goal context, report the mismatch, and leave the Goal waiting for its named actor or external trigger. If a ready Result Proposal exists, stop and wait for human Acceptance.`;
|
|
16095
16370
|
var ISSUE_ASSIGNEE_EXECUTION_RAIL = "Before doing issue-scoped execution as the assignee, check out the assigned issue. If checkout returns `409`, do not retry; stop and report the ownership conflict.";
|
|
16096
|
-
var ISSUE_ASSIGN_PROMPT_TEMPLATE =
|
|
16371
|
+
var ISSUE_ASSIGN_PROMPT_TEMPLATE = `<wake_context>
|
|
16372
|
+
You are agent {{agent.id}} ({{agent.name}}). You have been assigned to work on an issue.
|
|
16097
16373
|
|
|
16098
16374
|
{{context.rudderWorkspace.orgResourcesPrompt}}
|
|
16099
16375
|
|
|
16100
|
-
## Task Context
|
|
16101
|
-
|
|
16102
16376
|
**Issue:** {{issue.title}}
|
|
16103
16377
|
**ID:** {{issue.id}}
|
|
16104
16378
|
**Status:** {{issue.status}}
|
|
@@ -16107,21 +16381,23 @@ var ISSUE_ASSIGN_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{agent.name}}).
|
|
|
16107
16381
|
**Reviewer:** {{issue.reviewerLabel}}
|
|
16108
16382
|
**Created At:** {{issue.createdAt}}
|
|
16109
16383
|
**Updated At:** {{issue.updatedAt}}
|
|
16384
|
+
</wake_context>
|
|
16110
16385
|
|
|
16386
|
+
<quoted_issue_context>
|
|
16111
16387
|
**Description:**
|
|
16112
16388
|
{{issue.description}}
|
|
16389
|
+
</quoted_issue_context>
|
|
16113
16390
|
|
|
16114
16391
|
|
|
16115
16392
|
Your task is to review this issue, understand what kind of work it asks for, and take the appropriate next action.
|
|
16116
16393
|
|
|
16117
16394
|
Do not assume every issue is a codebase task. If the issue is a question, screenshot check, review, planning request, coordination task, or another non-code request, answer or handle that request directly. Inspect the codebase and implement a change only when the issue actually asks for engineering work or when the relevant project resources make code changes necessary.
|
|
16118
16395
|
${ISSUE_ASSIGNEE_EXECUTION_RAIL}`;
|
|
16119
|
-
var ISSUE_COMMENTED_PROMPT_TEMPLATE =
|
|
16396
|
+
var ISSUE_COMMENTED_PROMPT_TEMPLATE = `<wake_context>
|
|
16397
|
+
You are agent {{agent.id}} ({{agent.name}}). There is a new comment on an issue you own.
|
|
16120
16398
|
|
|
16121
16399
|
{{context.rudderWorkspace.orgResourcesPrompt}}
|
|
16122
16400
|
|
|
16123
|
-
## Context
|
|
16124
|
-
|
|
16125
16401
|
**Issue:** {{issue.title}}
|
|
16126
16402
|
**ID:** {{issue.id}}
|
|
16127
16403
|
**Status:** {{issue.status}}
|
|
@@ -16129,7 +16405,9 @@ var ISSUE_COMMENTED_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{agent.name}
|
|
|
16129
16405
|
**Reviewer:** {{issue.reviewerLabel}}
|
|
16130
16406
|
**Created At:** {{issue.createdAt}}
|
|
16131
16407
|
**Updated At:** {{issue.updatedAt}}
|
|
16408
|
+
</wake_context>
|
|
16132
16409
|
|
|
16410
|
+
<quoted_issue_context>
|
|
16133
16411
|
**Issue Description:**
|
|
16134
16412
|
{{issue.description}}
|
|
16135
16413
|
|
|
@@ -16138,15 +16416,15 @@ var ISSUE_COMMENTED_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{agent.name}
|
|
|
16138
16416
|
From: {{comment.authorLabel}} ({{comment.authorKind}})
|
|
16139
16417
|
|
|
16140
16418
|
{{comment.body}}
|
|
16419
|
+
</quoted_issue_context>
|
|
16141
16420
|
|
|
16142
16421
|
Review the new comment and continue the issue from the current state. Respond or take action as needed.
|
|
16143
16422
|
${ISSUE_ASSIGNEE_EXECUTION_RAIL}`;
|
|
16144
|
-
var ISSUE_CHANGES_REQUESTED_PROMPT_TEMPLATE =
|
|
16423
|
+
var ISSUE_CHANGES_REQUESTED_PROMPT_TEMPLATE = `<wake_context>
|
|
16424
|
+
You are agent {{agent.id}} ({{agent.name}}). A reviewer requested changes on an issue you own.
|
|
16145
16425
|
|
|
16146
16426
|
{{context.rudderWorkspace.orgResourcesPrompt}}
|
|
16147
16427
|
|
|
16148
|
-
## Context
|
|
16149
|
-
|
|
16150
16428
|
**Issue:** {{issue.title}}
|
|
16151
16429
|
**ID:** {{issue.id}}
|
|
16152
16430
|
**Status:** {{issue.status}}
|
|
@@ -16154,7 +16432,9 @@ var ISSUE_CHANGES_REQUESTED_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{age
|
|
|
16154
16432
|
**Reviewer:** {{issue.reviewerLabel}}
|
|
16155
16433
|
**Created At:** {{issue.createdAt}}
|
|
16156
16434
|
**Updated At:** {{issue.updatedAt}}
|
|
16435
|
+
</wake_context>
|
|
16157
16436
|
|
|
16437
|
+
<quoted_issue_context>
|
|
16158
16438
|
**Issue Description:**
|
|
16159
16439
|
{{issue.description}}
|
|
16160
16440
|
|
|
@@ -16163,10 +16443,12 @@ var ISSUE_CHANGES_REQUESTED_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{age
|
|
|
16163
16443
|
From: {{comment.authorLabel}} ({{comment.authorKind}})
|
|
16164
16444
|
|
|
16165
16445
|
{{comment.body}}
|
|
16446
|
+
</quoted_issue_context>
|
|
16166
16447
|
|
|
16167
16448
|
Review the requested changes and continue the issue from the current state. Address the reviewer feedback before handing it back for review.
|
|
16168
16449
|
${ISSUE_ASSIGNEE_EXECUTION_RAIL}`;
|
|
16169
|
-
var ISSUE_RECOVERY_PROMPT_TEMPLATE =
|
|
16450
|
+
var ISSUE_RECOVERY_PROMPT_TEMPLATE = `<wake_context>
|
|
16451
|
+
You are agent {{agent.id}} ({{agent.name}}). This is a recovery run, not a fresh task.
|
|
16170
16452
|
|
|
16171
16453
|
{{context.rudderWorkspace.orgResourcesPrompt}}
|
|
16172
16454
|
|
|
@@ -16188,14 +16470,18 @@ var ISSUE_RECOVERY_PROMPT_TEMPLATE = `You are agent {{agent.id}} ({{agent.name}}
|
|
|
16188
16470
|
- Reviewer: {{issue.reviewerLabel}}
|
|
16189
16471
|
- Created At: {{issue.createdAt}}
|
|
16190
16472
|
- Updated At: {{issue.updatedAt}}
|
|
16473
|
+
</wake_context>
|
|
16191
16474
|
|
|
16475
|
+
<quoted_issue_context>
|
|
16192
16476
|
- Description:
|
|
16193
16477
|
{{issue.description}}
|
|
16478
|
+
</quoted_issue_context>
|
|
16194
16479
|
|
|
16195
16480
|
|
|
16196
16481
|
Before doing anything else, inspect what the previous run already completed and any side effects it may have caused. Continue the remaining work from the current state. Avoid blindly re-running the whole task.
|
|
16197
16482
|
${ISSUE_ASSIGNEE_EXECUTION_RAIL}`;
|
|
16198
|
-
var ISSUE_PASSIVE_FOLLOWUP_PROMPT_TEMPLATE =
|
|
16483
|
+
var ISSUE_PASSIVE_FOLLOWUP_PROMPT_TEMPLATE = `<wake_context>
|
|
16484
|
+
You are agent {{agent.id}} ({{agent.name}}). This is a passive issue follow-up, not a fresh assignment and not a failure recovery.
|
|
16199
16485
|
|
|
16200
16486
|
{{context.rudderWorkspace.orgResourcesPrompt}}
|
|
16201
16487
|
|
|
@@ -16218,16 +16504,17 @@ Reason: {{context.passiveFollowup.reason}}
|
|
|
16218
16504
|
- Reviewer: {{issue.reviewerLabel}}
|
|
16219
16505
|
- Created At: {{issue.createdAt}}
|
|
16220
16506
|
- Updated At: {{issue.updatedAt}}
|
|
16507
|
+
</wake_context>
|
|
16221
16508
|
|
|
16509
|
+
<quoted_issue_context>
|
|
16222
16510
|
- Description:
|
|
16223
16511
|
{{issue.description}}
|
|
16512
|
+
</quoted_issue_context>
|
|
16224
16513
|
|
|
16225
16514
|
|
|
16226
16515
|
Before changing the issue, continue to progress the current issue, then inspect the current issue state and any side effects from the previous run. Finally, do exactly one close-out action: add a progress comment, mark the issue done, block it with a reason, or hand it off explicitly with explanation.
|
|
16227
16516
|
${ISSUE_ASSIGNEE_EXECUTION_RAIL}`;
|
|
16228
16517
|
var RUDDER_AGENT_OPERATING_CONTRACT = [
|
|
16229
|
-
"# Rudder Agent Operating Contract",
|
|
16230
|
-
"",
|
|
16231
16518
|
"You are a helpful assistant running inside Rudder. Your home directory is `$AGENT_HOME`. Everything personal to you -- life, memory, knowledge -- lives there. Other agents may have their own folders and you may update them when necessary.",
|
|
16232
16519
|
"",
|
|
16233
16520
|
"Read Rudder mcp tools to firstly.",
|
|
@@ -16288,8 +16575,6 @@ var RUDDER_AGENT_OPERATING_CONTRACT = [
|
|
|
16288
16575
|
"- When the user explicitly mentions previously handled issue, tasks or conversations, you need to retrieve the relevant tasks first before proceeding with the next action."
|
|
16289
16576
|
].join("\n");
|
|
16290
16577
|
var RUDDER_AGENT_HEARTBEAT_INSTRUCTION = [
|
|
16291
|
-
"# Rudder Heartbeat Instruction",
|
|
16292
|
-
"",
|
|
16293
16578
|
"This section is injected by Rudder only for heartbeat scene runs. It is the platform-owned heartbeat/self-check pipeline.",
|
|
16294
16579
|
"",
|
|
16295
16580
|
"## Heartbeat Pipeline",
|
|
@@ -16315,8 +16600,8 @@ var RUDDER_AGENT_HEARTBEAT_INSTRUCTION = [
|
|
|
16315
16600
|
// ../packages/agent-runtime-utils/dist/server-utils.cli.js
|
|
16316
16601
|
async function resolveRudderSkillsDir(moduleDir, additionalCandidates = []) {
|
|
16317
16602
|
const candidates = [
|
|
16318
|
-
...RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) =>
|
|
16319
|
-
...additionalCandidates.map((candidate) =>
|
|
16603
|
+
...RUDDER_SKILL_ROOT_RELATIVE_CANDIDATES.map((relativePath) => path11.resolve(moduleDir, relativePath)),
|
|
16604
|
+
...additionalCandidates.map((candidate) => path11.resolve(candidate))
|
|
16320
16605
|
];
|
|
16321
16606
|
const seenRoots = /* @__PURE__ */ new Set();
|
|
16322
16607
|
for (const root of candidates) {
|
|
@@ -16333,26 +16618,26 @@ async function removeMaintainerOnlySkillSymlinks(skillsHome, allowedSkillNames)
|
|
|
16333
16618
|
return removeUnselectedRudderSkillSymlinks(skillsHome, allowedSkillNames);
|
|
16334
16619
|
}
|
|
16335
16620
|
async function readRudderMaterializedSkillSource(target) {
|
|
16336
|
-
const manifestPath =
|
|
16621
|
+
const manifestPath = path11.join(target, ".rudder", "materialized-skill.json");
|
|
16337
16622
|
const raw = await fs8.readFile(manifestPath, "utf8").catch(() => null);
|
|
16338
16623
|
if (!raw)
|
|
16339
16624
|
return null;
|
|
16340
16625
|
try {
|
|
16341
16626
|
const parsed = parseObject(JSON.parse(raw));
|
|
16342
16627
|
const sourcePath = asString(parsed.sourcePath, "").trim();
|
|
16343
|
-
return sourcePath.length > 0 ?
|
|
16628
|
+
return sourcePath.length > 0 ? path11.resolve(sourcePath) : null;
|
|
16344
16629
|
} catch {
|
|
16345
16630
|
return null;
|
|
16346
16631
|
}
|
|
16347
16632
|
}
|
|
16348
16633
|
async function removeUnselectedRudderSkillSymlinks(skillsHome, allowedSkillNames, knownSkillSources = []) {
|
|
16349
16634
|
const allowed = new Set(Array.from(allowedSkillNames));
|
|
16350
|
-
const knownSources = new Set(Array.from(knownSkillSources).map((value) => value.trim()).filter(Boolean).map((value) =>
|
|
16635
|
+
const knownSources = new Set(Array.from(knownSkillSources).map((value) => value.trim()).filter(Boolean).map((value) => path11.resolve(value)));
|
|
16351
16636
|
try {
|
|
16352
16637
|
const entries = await fs8.readdir(skillsHome, { withFileTypes: true });
|
|
16353
16638
|
const removed = [];
|
|
16354
16639
|
for (const entry of entries) {
|
|
16355
|
-
const target =
|
|
16640
|
+
const target = path11.join(skillsHome, entry.name);
|
|
16356
16641
|
const existing = await fs8.lstat(target).catch(() => null);
|
|
16357
16642
|
if (!existing)
|
|
16358
16643
|
continue;
|
|
@@ -16361,8 +16646,8 @@ async function removeUnselectedRudderSkillSymlinks(skillsHome, allowedSkillNames
|
|
|
16361
16646
|
const linkedPath = await fs8.readlink(target).catch(() => null);
|
|
16362
16647
|
if (!linkedPath)
|
|
16363
16648
|
continue;
|
|
16364
|
-
const resolvedLinkedPath =
|
|
16365
|
-
isRudderManagedSkill = knownSources.has(
|
|
16649
|
+
const resolvedLinkedPath = path11.isAbsolute(linkedPath) ? linkedPath : path11.resolve(path11.dirname(target), linkedPath);
|
|
16650
|
+
isRudderManagedSkill = knownSources.has(path11.resolve(resolvedLinkedPath)) || isMaintainerOnlySkillTarget(linkedPath) || isMaintainerOnlySkillTarget(resolvedLinkedPath);
|
|
16366
16651
|
} else if (existing.isDirectory()) {
|
|
16367
16652
|
const materializedSource = await readRudderMaterializedSkillSource(target);
|
|
16368
16653
|
isRudderManagedSkill = materializedSource !== null && knownSources.has(materializedSource);
|
|
@@ -16384,7 +16669,7 @@ async function removeUnselectedRudderSkillSymlinks(skillsHome, allowedSkillNames
|
|
|
16384
16669
|
init_dist2();
|
|
16385
16670
|
import fs9 from "node:fs/promises";
|
|
16386
16671
|
import os3 from "node:os";
|
|
16387
|
-
import
|
|
16672
|
+
import path12 from "node:path";
|
|
16388
16673
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
16389
16674
|
|
|
16390
16675
|
// src/commands/client/help.ts
|
|
@@ -16408,16 +16693,16 @@ function formatHelpExample(example) {
|
|
|
16408
16693
|
}
|
|
16409
16694
|
|
|
16410
16695
|
// src/commands/client/agent.ts
|
|
16411
|
-
var __moduleDir =
|
|
16696
|
+
var __moduleDir = path12.dirname(fileURLToPath6(import.meta.url));
|
|
16412
16697
|
function codexSkillsHome() {
|
|
16413
16698
|
const fromEnv = process.env.CODEX_HOME?.trim();
|
|
16414
|
-
const base = fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
16415
|
-
return
|
|
16699
|
+
const base = fromEnv && fromEnv.length > 0 ? fromEnv : path12.join(os3.homedir(), ".codex");
|
|
16700
|
+
return path12.join(base, "skills");
|
|
16416
16701
|
}
|
|
16417
16702
|
function claudeSkillsHome() {
|
|
16418
16703
|
const fromEnv = process.env.CLAUDE_HOME?.trim();
|
|
16419
|
-
const base = fromEnv && fromEnv.length > 0 ? fromEnv :
|
|
16420
|
-
return
|
|
16704
|
+
const base = fromEnv && fromEnv.length > 0 ? fromEnv : path12.join(os3.homedir(), ".claude");
|
|
16705
|
+
return path12.join(base, "skills");
|
|
16421
16706
|
}
|
|
16422
16707
|
async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool2) {
|
|
16423
16708
|
const summary = {
|
|
@@ -16436,8 +16721,8 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool2) {
|
|
|
16436
16721
|
);
|
|
16437
16722
|
for (const entry of entries) {
|
|
16438
16723
|
if (!entry.isDirectory()) continue;
|
|
16439
|
-
const source =
|
|
16440
|
-
const target =
|
|
16724
|
+
const source = path12.join(sourceSkillsDir, entry.name);
|
|
16725
|
+
const target = path12.join(targetSkillsDir, entry.name);
|
|
16441
16726
|
const existing = await fs9.lstat(target).catch(() => null);
|
|
16442
16727
|
if (existing) {
|
|
16443
16728
|
if (existing.isSymbolicLink()) {
|
|
@@ -16458,7 +16743,7 @@ async function installSkillsForTarget(sourceSkillsDir, targetSkillsDir, tool2) {
|
|
|
16458
16743
|
continue;
|
|
16459
16744
|
}
|
|
16460
16745
|
}
|
|
16461
|
-
const resolvedLinkedPath =
|
|
16746
|
+
const resolvedLinkedPath = path12.isAbsolute(linkedPath) ? linkedPath : path12.resolve(path12.dirname(target), linkedPath);
|
|
16462
16747
|
const linkedTargetExists = await fs9.stat(resolvedLinkedPath).then(() => true).catch(() => false);
|
|
16463
16748
|
if (!linkedTargetExists) {
|
|
16464
16749
|
await fs9.unlink(target);
|
|
@@ -16683,7 +16968,7 @@ function registerAgentCommands(program) {
|
|
|
16683
16968
|
if (opts.markdown && opts.markdownFile) {
|
|
16684
16969
|
throw new Error("Pass only one of --markdown or --markdown-file.");
|
|
16685
16970
|
}
|
|
16686
|
-
const markdown = opts.markdownFile ? await fs9.readFile(
|
|
16971
|
+
const markdown = opts.markdownFile ? await fs9.readFile(path12.resolve(opts.markdownFile), "utf8") : opts.markdown;
|
|
16687
16972
|
const payload = organizationSkillCreateSchema.parse({
|
|
16688
16973
|
name: opts.name,
|
|
16689
16974
|
slug: opts.slug?.trim() || null,
|
|
@@ -16862,7 +17147,7 @@ function registerAgentCommands(program) {
|
|
|
16862
17147
|
}
|
|
16863
17148
|
const installSummaries = [];
|
|
16864
17149
|
if (opts.installSkills !== false) {
|
|
16865
|
-
const skillsDir = await resolveRudderSkillsDir(__moduleDir, [
|
|
17150
|
+
const skillsDir = await resolveRudderSkillsDir(__moduleDir, [path12.resolve(process.cwd(), "skills")]);
|
|
16866
17151
|
if (!skillsDir) {
|
|
16867
17152
|
throw new Error(
|
|
16868
17153
|
"Could not locate local Rudder skills directory. Expected ./skills in the repo checkout."
|
|
@@ -16950,10 +17235,10 @@ async function buildAgentUpdatePatch(opts) {
|
|
|
16950
17235
|
if (opts.capabilities !== void 0) rawPatch.capabilities = opts.capabilities;
|
|
16951
17236
|
if (opts.description !== void 0) rawPatch.capabilities = opts.description;
|
|
16952
17237
|
if (opts.capabilitiesFile !== void 0) {
|
|
16953
|
-
rawPatch.capabilities = await fs9.readFile(
|
|
17238
|
+
rawPatch.capabilities = await fs9.readFile(path12.resolve(opts.capabilitiesFile), "utf8");
|
|
16954
17239
|
}
|
|
16955
17240
|
if (opts.descriptionFile !== void 0) {
|
|
16956
|
-
rawPatch.capabilities = await fs9.readFile(
|
|
17241
|
+
rawPatch.capabilities = await fs9.readFile(path12.resolve(opts.descriptionFile), "utf8");
|
|
16957
17242
|
}
|
|
16958
17243
|
if (clearCapabilities) rawPatch.capabilities = null;
|
|
16959
17244
|
return updateAgentSchema.parse(rawPatch);
|
|
@@ -16977,7 +17262,7 @@ function parseJsonObject(value, name) {
|
|
|
16977
17262
|
// src/commands/client/approval.ts
|
|
16978
17263
|
init_dist2();
|
|
16979
17264
|
import { readFile as readFile2 } from "node:fs/promises";
|
|
16980
|
-
import
|
|
17265
|
+
import path13 from "node:path";
|
|
16981
17266
|
function registerApprovalCommands(program) {
|
|
16982
17267
|
const approval = program.command("approval").description("Approval operations");
|
|
16983
17268
|
addCommonClientOptions(
|
|
@@ -17168,7 +17453,7 @@ async function readTextInputFile(inputPath, optionName) {
|
|
|
17168
17453
|
if (inputPath === "-") {
|
|
17169
17454
|
return readStdinText();
|
|
17170
17455
|
}
|
|
17171
|
-
const resolvedPath =
|
|
17456
|
+
const resolvedPath = path13.resolve(process.cwd(), inputPath);
|
|
17172
17457
|
return readFile2(resolvedPath, "utf8").catch((err) => {
|
|
17173
17458
|
throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
17174
17459
|
});
|
|
@@ -18005,12 +18290,12 @@ async function readStdin() {
|
|
|
18005
18290
|
|
|
18006
18291
|
// src/commands/client/company.ts
|
|
18007
18292
|
import * as p3 from "@clack/prompts";
|
|
18008
|
-
import { mkdir as
|
|
18009
|
-
import
|
|
18293
|
+
import { mkdir as mkdir2, readdir as readdir2, readFile as readFile3, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
18294
|
+
import path15 from "node:path";
|
|
18010
18295
|
import pc5 from "picocolors";
|
|
18011
18296
|
|
|
18012
18297
|
// src/commands/client/zip.ts
|
|
18013
|
-
import
|
|
18298
|
+
import path14 from "node:path";
|
|
18014
18299
|
import { inflateRawSync } from "node:zlib";
|
|
18015
18300
|
var textDecoder = new TextDecoder();
|
|
18016
18301
|
var binaryContentTypeByExtension = {
|
|
@@ -18038,7 +18323,7 @@ function sharedArchiveRoot(paths) {
|
|
|
18038
18323
|
return firstSegments.every((parts) => parts.length > 1 && parts[0] === candidate) ? candidate : null;
|
|
18039
18324
|
}
|
|
18040
18325
|
function bytesToPortableFileEntry(pathValue, bytes) {
|
|
18041
|
-
const contentType = binaryContentTypeByExtension[
|
|
18326
|
+
const contentType = binaryContentTypeByExtension[path14.extname(pathValue).toLowerCase()];
|
|
18042
18327
|
if (!contentType) return textDecoder.decode(bytes);
|
|
18043
18328
|
return {
|
|
18044
18329
|
encoding: "base64",
|
|
@@ -18126,7 +18411,7 @@ var IMPORT_INCLUDE_OPTIONS = [
|
|
|
18126
18411
|
];
|
|
18127
18412
|
var IMPORT_PREVIEW_SAMPLE_LIMIT = 6;
|
|
18128
18413
|
function readPortableFileEntry(filePath, contents) {
|
|
18129
|
-
const contentType = binaryContentTypeByExtension[
|
|
18414
|
+
const contentType = binaryContentTypeByExtension[path15.extname(filePath).toLowerCase()];
|
|
18130
18415
|
if (!contentType) return contents.toString("utf8");
|
|
18131
18416
|
return {
|
|
18132
18417
|
encoding: "base64",
|
|
@@ -18181,10 +18466,10 @@ function normalizePortablePath(filePath) {
|
|
|
18181
18466
|
return filePath.replace(/\\/g, "/");
|
|
18182
18467
|
}
|
|
18183
18468
|
function shouldIncludePortableFile(filePath) {
|
|
18184
|
-
const baseName =
|
|
18469
|
+
const baseName = path15.basename(filePath);
|
|
18185
18470
|
const isMarkdown = baseName.endsWith(".md");
|
|
18186
18471
|
const isPaperclipYaml = baseName === ".rudder.yaml" || baseName === ".rudder.yml";
|
|
18187
|
-
const contentType = binaryContentTypeByExtension[
|
|
18472
|
+
const contentType = binaryContentTypeByExtension[path15.extname(baseName).toLowerCase()];
|
|
18188
18473
|
return isMarkdown || isPaperclipYaml || Boolean(contentType);
|
|
18189
18474
|
}
|
|
18190
18475
|
function findPortableExtensionPath(files) {
|
|
@@ -18739,7 +19024,7 @@ function normalizeGithubImportSource(input, refOverride) {
|
|
|
18739
19024
|
}
|
|
18740
19025
|
async function pathExists(inputPath) {
|
|
18741
19026
|
try {
|
|
18742
|
-
await
|
|
19027
|
+
await stat2(path15.resolve(inputPath));
|
|
18743
19028
|
return true;
|
|
18744
19029
|
} catch {
|
|
18745
19030
|
return false;
|
|
@@ -18749,45 +19034,45 @@ async function collectPackageFiles(root, current, files) {
|
|
|
18749
19034
|
const entries = await readdir2(current, { withFileTypes: true });
|
|
18750
19035
|
for (const entry of entries) {
|
|
18751
19036
|
if (entry.name.startsWith(".git")) continue;
|
|
18752
|
-
const absolutePath =
|
|
19037
|
+
const absolutePath = path15.join(current, entry.name);
|
|
18753
19038
|
if (entry.isDirectory()) {
|
|
18754
19039
|
await collectPackageFiles(root, absolutePath, files);
|
|
18755
19040
|
continue;
|
|
18756
19041
|
}
|
|
18757
19042
|
if (!entry.isFile()) continue;
|
|
18758
|
-
const relativePath =
|
|
19043
|
+
const relativePath = path15.relative(root, absolutePath).replace(/\\/g, "/");
|
|
18759
19044
|
if (!shouldIncludePortableFile(relativePath)) continue;
|
|
18760
19045
|
files[relativePath] = readPortableFileEntry(relativePath, await readFile3(absolutePath));
|
|
18761
19046
|
}
|
|
18762
19047
|
}
|
|
18763
19048
|
async function resolveInlineSourceFromPath(inputPath) {
|
|
18764
|
-
const resolved =
|
|
18765
|
-
const resolvedStat = await
|
|
18766
|
-
if (resolvedStat.isFile() &&
|
|
19049
|
+
const resolved = path15.resolve(inputPath);
|
|
19050
|
+
const resolvedStat = await stat2(resolved);
|
|
19051
|
+
if (resolvedStat.isFile() && path15.extname(resolved).toLowerCase() === ".zip") {
|
|
18767
19052
|
const archive = await readZipArchive(await readFile3(resolved));
|
|
18768
19053
|
const filteredFiles = Object.fromEntries(
|
|
18769
19054
|
Object.entries(archive.files).filter(([relativePath]) => shouldIncludePortableFile(relativePath))
|
|
18770
19055
|
);
|
|
18771
19056
|
return {
|
|
18772
|
-
rootPath: archive.rootPath ??
|
|
19057
|
+
rootPath: archive.rootPath ?? path15.basename(resolved, ".zip"),
|
|
18773
19058
|
files: filteredFiles
|
|
18774
19059
|
};
|
|
18775
19060
|
}
|
|
18776
|
-
const rootDir = resolvedStat.isDirectory() ? resolved :
|
|
19061
|
+
const rootDir = resolvedStat.isDirectory() ? resolved : path15.dirname(resolved);
|
|
18777
19062
|
const files = {};
|
|
18778
19063
|
await collectPackageFiles(rootDir, rootDir, files);
|
|
18779
19064
|
return {
|
|
18780
|
-
rootPath:
|
|
19065
|
+
rootPath: path15.basename(rootDir),
|
|
18781
19066
|
files
|
|
18782
19067
|
};
|
|
18783
19068
|
}
|
|
18784
19069
|
async function writeExportToFolder(outDir, exported) {
|
|
18785
|
-
const root =
|
|
18786
|
-
await
|
|
19070
|
+
const root = path15.resolve(outDir);
|
|
19071
|
+
await mkdir2(root, { recursive: true });
|
|
18787
19072
|
for (const [relativePath, content] of Object.entries(exported.files)) {
|
|
18788
19073
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
18789
|
-
const filePath =
|
|
18790
|
-
await
|
|
19074
|
+
const filePath = path15.join(root, normalized);
|
|
19075
|
+
await mkdir2(path15.dirname(filePath), { recursive: true });
|
|
18791
19076
|
const writeValue = portableFileEntryToWriteValue(content);
|
|
18792
19077
|
if (typeof writeValue === "string") {
|
|
18793
19078
|
await writeFile2(filePath, writeValue, "utf8");
|
|
@@ -18797,8 +19082,8 @@ async function writeExportToFolder(outDir, exported) {
|
|
|
18797
19082
|
}
|
|
18798
19083
|
}
|
|
18799
19084
|
async function confirmOverwriteExportDirectory(outDir) {
|
|
18800
|
-
const root =
|
|
18801
|
-
const stats = await
|
|
19085
|
+
const root = path15.resolve(outDir);
|
|
19086
|
+
const stats = await stat2(root).catch(() => null);
|
|
18802
19087
|
if (!stats) return;
|
|
18803
19088
|
if (!stats.isDirectory()) {
|
|
18804
19089
|
throw new Error(`Export output path ${root} exists and is not a directory.`);
|
|
@@ -18979,7 +19264,7 @@ function registerCompanyCommands(program) {
|
|
|
18979
19264
|
printOutput(
|
|
18980
19265
|
{
|
|
18981
19266
|
ok: true,
|
|
18982
|
-
out:
|
|
19267
|
+
out: path15.resolve(opts.out),
|
|
18983
19268
|
rootPath: exported.rootPath,
|
|
18984
19269
|
filesWritten: Object.keys(exported.files).length,
|
|
18985
19270
|
rudderExtensionPath: exported.rudderExtensionPath,
|
|
@@ -19477,8 +19762,8 @@ function parseResultValue(value) {
|
|
|
19477
19762
|
|
|
19478
19763
|
// src/commands/client/issue.ts
|
|
19479
19764
|
init_dist2();
|
|
19480
|
-
import { readFile as readFile4, stat as
|
|
19481
|
-
import
|
|
19765
|
+
import { readFile as readFile4, stat as stat3 } from "node:fs/promises";
|
|
19766
|
+
import path16 from "node:path";
|
|
19482
19767
|
function registerIssueCommands(program) {
|
|
19483
19768
|
const issue = program.command("issue").description("Issue operations");
|
|
19484
19769
|
addCommonClientOptions(
|
|
@@ -19842,7 +20127,7 @@ async function readTextInputFile2(inputPath, optionName) {
|
|
|
19842
20127
|
if (inputPath === "-") {
|
|
19843
20128
|
return readStdinText2();
|
|
19844
20129
|
}
|
|
19845
|
-
const resolvedPath =
|
|
20130
|
+
const resolvedPath = path16.resolve(process.cwd(), inputPath);
|
|
19846
20131
|
return readFile4(resolvedPath, "utf8").catch((err) => {
|
|
19847
20132
|
throw new Error(`Unable to read ${optionName} ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
19848
20133
|
});
|
|
@@ -19899,14 +20184,14 @@ async function appendUploadedIssueImages(ctx, issueId, body, imagePaths) {
|
|
|
19899
20184
|
${imageBlock}` : imageBlock;
|
|
19900
20185
|
}
|
|
19901
20186
|
async function uploadIssueCommentImage(ctx, issue, imagePath) {
|
|
19902
|
-
const resolvedPath =
|
|
19903
|
-
const stats = await
|
|
20187
|
+
const resolvedPath = path16.resolve(process.cwd(), imagePath);
|
|
20188
|
+
const stats = await stat3(resolvedPath).catch((err) => {
|
|
19904
20189
|
throw new Error(`Unable to read image ${imagePath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
19905
20190
|
});
|
|
19906
20191
|
if (!stats.isFile()) {
|
|
19907
20192
|
throw new Error(`Image path must be a file: ${imagePath}`);
|
|
19908
20193
|
}
|
|
19909
|
-
const filename =
|
|
20194
|
+
const filename = path16.basename(resolvedPath);
|
|
19910
20195
|
const contentType = inferCommentImageContentType(filename);
|
|
19911
20196
|
const buffer = await readFile4(resolvedPath);
|
|
19912
20197
|
if (buffer.length <= 0) {
|
|
@@ -19925,7 +20210,7 @@ async function uploadIssueCommentImage(ctx, issue, imagePath) {
|
|
|
19925
20210
|
return attachment;
|
|
19926
20211
|
}
|
|
19927
20212
|
function inferCommentImageContentType(filename) {
|
|
19928
|
-
const ext =
|
|
20213
|
+
const ext = path16.extname(filename).toLowerCase();
|
|
19929
20214
|
switch (ext) {
|
|
19930
20215
|
case ".png":
|
|
19931
20216
|
return "image/png";
|
|
@@ -20029,7 +20314,7 @@ function formatIssueSearchMatch(match) {
|
|
|
20029
20314
|
|
|
20030
20315
|
// src/commands/client/library.ts
|
|
20031
20316
|
import { readFile as readFile5 } from "node:fs/promises";
|
|
20032
|
-
import
|
|
20317
|
+
import path17 from "node:path";
|
|
20033
20318
|
function toLibraryFileLinkResult(detail) {
|
|
20034
20319
|
return {
|
|
20035
20320
|
filePath: detail.filePath,
|
|
@@ -20174,7 +20459,7 @@ async function resolveBodyFileInput(inputPath) {
|
|
|
20174
20459
|
if (inputPath === "-") {
|
|
20175
20460
|
return readStdinText3();
|
|
20176
20461
|
}
|
|
20177
|
-
const resolvedPath =
|
|
20462
|
+
const resolvedPath = path17.resolve(process.cwd(), inputPath);
|
|
20178
20463
|
return readFile5(resolvedPath, "utf8").catch((err) => {
|
|
20179
20464
|
throw new Error(`Unable to read --body-file ${inputPath}: ${err instanceof Error ? err.message : String(err)}`);
|
|
20180
20465
|
});
|
|
@@ -20933,8 +21218,8 @@ function registerUserCommands(program) {
|
|
|
20933
21218
|
appendParam(params, "limit", opts.limit);
|
|
20934
21219
|
appendParam(params, "cursor", opts.cursor);
|
|
20935
21220
|
const query = params.toString();
|
|
20936
|
-
const
|
|
20937
|
-
const result = await ctx.api.get(
|
|
21221
|
+
const path25 = `/api/orgs/${ctx.orgId}/users/${encodeURIComponent(userId)}/activity-ledger${query ? `?${query}` : ""}`;
|
|
21222
|
+
const result = await ctx.api.get(path25);
|
|
20938
21223
|
if (ctx.json) {
|
|
20939
21224
|
printOutput(result, { json: true });
|
|
20940
21225
|
return;
|
|
@@ -21751,9 +22036,9 @@ import * as p15 from "@clack/prompts";
|
|
|
21751
22036
|
import { spawn as spawn3, spawnSync as spawnSync4 } from "node:child_process";
|
|
21752
22037
|
import { createHash as createHash4, randomUUID } from "node:crypto";
|
|
21753
22038
|
import { constants as fsConstants, mkdirSync as mkdirSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
21754
|
-
import { access, chmod
|
|
22039
|
+
import { access, chmod as chmod2, copyFile, cp, lstat, mkdir as mkdir3, mkdtemp as mkdtemp2, readdir as readdir3, readFile as readFile6, rm as rm3, stat as stat4, utimes, writeFile as writeFile3 } from "node:fs/promises";
|
|
21755
22040
|
import { homedir, tmpdir } from "node:os";
|
|
21756
|
-
import
|
|
22041
|
+
import path23 from "node:path";
|
|
21757
22042
|
import { clearTimeout as clearTimeout3, setTimeout as setTimeout3 } from "node:timers";
|
|
21758
22043
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
21759
22044
|
import pc14 from "picocolors";
|
|
@@ -21782,11 +22067,11 @@ init_home();
|
|
|
21782
22067
|
|
|
21783
22068
|
// src/desktop-download.ts
|
|
21784
22069
|
import { createHash as createHash3 } from "node:crypto";
|
|
21785
|
-
import { createWriteStream as
|
|
22070
|
+
import { createWriteStream as createWriteStream3, mkdirSync } from "node:fs";
|
|
21786
22071
|
import { rm as rm2 } from "node:fs/promises";
|
|
21787
|
-
import
|
|
22072
|
+
import path22 from "node:path";
|
|
21788
22073
|
import { Readable as Readable2, Transform as Transform2 } from "node:stream";
|
|
21789
|
-
import { pipeline as
|
|
22074
|
+
import { pipeline as pipeline3 } from "node:stream/promises";
|
|
21790
22075
|
import { clearTimeout as clearTimeout2, setTimeout as setTimeout2 } from "node:timers";
|
|
21791
22076
|
|
|
21792
22077
|
// src/utils/progress.ts
|
|
@@ -22024,7 +22309,7 @@ async function resolveDesktopDownloadOrigins(options) {
|
|
|
22024
22309
|
}
|
|
22025
22310
|
async function downloadAsset(asset, outputDir, progressFactory = createByteProgress, expectedChecksum, timeouts = {}) {
|
|
22026
22311
|
mkdirSync(outputDir, { recursive: true });
|
|
22027
|
-
const outputPath =
|
|
22312
|
+
const outputPath = path22.join(outputDir, path22.basename(asset.name));
|
|
22028
22313
|
const idleTimeoutMs = timeouts.idleMs ?? DESKTOP_ASSET_IDLE_TIMEOUT_MS;
|
|
22029
22314
|
const responseTimeoutMs = timeouts.responseMs ?? DESKTOP_ASSET_RESPONSE_TIMEOUT_MS;
|
|
22030
22315
|
const failures = [];
|
|
@@ -22064,7 +22349,7 @@ async function downloadAsset(asset, outputDir, progressFactory = createByteProgr
|
|
|
22064
22349
|
});
|
|
22065
22350
|
progress.start(totalBytes);
|
|
22066
22351
|
armIdleTimeout();
|
|
22067
|
-
await
|
|
22352
|
+
await pipeline3(Readable2.fromWeb(response.body), monitor, createWriteStream3(outputPath));
|
|
22068
22353
|
if (idleTimeout) clearTimeout2(idleTimeout);
|
|
22069
22354
|
idleTimeout = null;
|
|
22070
22355
|
const actualChecksum = hash.digest("hex");
|
|
@@ -22128,6 +22413,15 @@ var DEFAULT_DESKTOP_ASSET_CACHE_MAX_BYTES = 768 * 1024 * 1024;
|
|
|
22128
22413
|
var DEFAULT_DESKTOP_ASSET_CACHE_KEEP_PREVIOUS = 1;
|
|
22129
22414
|
var DESKTOP_INSTALL_LOCK_TIMEOUT_MS = 60 * 60 * 1e3;
|
|
22130
22415
|
var DESKTOP_INSTALL_LOCK_POLL_MS = 250;
|
|
22416
|
+
var DESKTOP_RUNTIME_PREPARE_TIMEOUT_MS = 9e4;
|
|
22417
|
+
async function waitForDesktopRuntimeSmokeEvidence(envName) {
|
|
22418
|
+
if (process.env.RUDDER_DESKTOP_SMOKE_AUTO_UPDATE_PUBLIC !== "1") return;
|
|
22419
|
+
const value = Number(process.env[envName]);
|
|
22420
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
22421
|
+
await delay2(Math.min(value, 1e4));
|
|
22422
|
+
}
|
|
22423
|
+
var DEFAULT_GITHUB_API_BASE_URL = "https://api.github.com";
|
|
22424
|
+
var DEFAULT_GITHUB_DOWNLOAD_BASE_URL = "https://github.com";
|
|
22131
22425
|
function normalizeProgressTotal(totalBytes) {
|
|
22132
22426
|
return typeof totalBytes === "number" && Number.isFinite(totalBytes) && totalBytes > 0 ? totalBytes : null;
|
|
22133
22427
|
}
|
|
@@ -22351,38 +22645,38 @@ function resolveDesktopAssetTarget(platform = process.platform, arch = process.a
|
|
|
22351
22645
|
throw new Error(`Rudder Desktop does not publish portable assets for ${platform}.`);
|
|
22352
22646
|
}
|
|
22353
22647
|
function resolveDefaultDesktopInstallRoot(target, env = process.env, homeDir = homedir()) {
|
|
22354
|
-
if (target.platform === "macos") return
|
|
22648
|
+
if (target.platform === "macos") return path23.join(homeDir, "Applications");
|
|
22355
22649
|
if (target.platform === "windows") {
|
|
22356
|
-
const localAppData = env.LOCALAPPDATA?.trim() ||
|
|
22357
|
-
return
|
|
22650
|
+
const localAppData = env.LOCALAPPDATA?.trim() || path23.join(homeDir, "AppData", "Local");
|
|
22651
|
+
return path23.join(localAppData, "Programs", DESKTOP_APP_NAME);
|
|
22358
22652
|
}
|
|
22359
|
-
return
|
|
22653
|
+
return path23.join(homeDir, ".local", "share", "rudder");
|
|
22360
22654
|
}
|
|
22361
22655
|
function resolveDesktopInstallPaths(target, installRoot) {
|
|
22362
|
-
const root =
|
|
22656
|
+
const root = path23.resolve(installRoot);
|
|
22363
22657
|
if (target.platform === "macos") {
|
|
22364
|
-
const appPath2 =
|
|
22658
|
+
const appPath2 = path23.join(root, `${DESKTOP_APP_NAME}.app`);
|
|
22365
22659
|
return {
|
|
22366
22660
|
installRoot: root,
|
|
22367
22661
|
appPath: appPath2,
|
|
22368
|
-
executablePath:
|
|
22369
|
-
metadataPath:
|
|
22662
|
+
executablePath: path23.join(appPath2, "Contents", "MacOS", DESKTOP_APP_NAME),
|
|
22663
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
22370
22664
|
};
|
|
22371
22665
|
}
|
|
22372
22666
|
if (target.platform === "windows") {
|
|
22373
22667
|
return {
|
|
22374
22668
|
installRoot: root,
|
|
22375
22669
|
appPath: root,
|
|
22376
|
-
executablePath:
|
|
22377
|
-
metadataPath:
|
|
22670
|
+
executablePath: path23.join(root, `${DESKTOP_APP_NAME}.exe`),
|
|
22671
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
22378
22672
|
};
|
|
22379
22673
|
}
|
|
22380
|
-
const appPath =
|
|
22674
|
+
const appPath = path23.join(root, `${DESKTOP_APP_NAME}.AppImage`);
|
|
22381
22675
|
return {
|
|
22382
22676
|
installRoot: root,
|
|
22383
22677
|
appPath,
|
|
22384
22678
|
executablePath: appPath,
|
|
22385
|
-
metadataPath:
|
|
22679
|
+
metadataPath: path23.join(root, DESKTOP_METADATA_FILE)
|
|
22386
22680
|
};
|
|
22387
22681
|
}
|
|
22388
22682
|
function normalizeAssetName(name) {
|
|
@@ -22441,10 +22735,20 @@ function resolveDesktopAssetCandidates(options) {
|
|
|
22441
22735
|
const candidates = [];
|
|
22442
22736
|
const deterministicShellName = options.directReleaseVersion ? resolveDesktopShellAssetName(options.directReleaseVersion, options.target) : null;
|
|
22443
22737
|
if (options.allowShellAssets !== false) {
|
|
22444
|
-
const shellAsset = selectDesktopShellAsset(options.releaseAssets, options.target) ?? (options.releaseAssets.length === 0 && deterministicShellName ? buildGithubReleaseAsset(
|
|
22738
|
+
const shellAsset = selectDesktopShellAsset(options.releaseAssets, options.target) ?? (options.releaseAssets.length === 0 && deterministicShellName ? buildGithubReleaseAsset(
|
|
22739
|
+
options.repo,
|
|
22740
|
+
options.tag,
|
|
22741
|
+
deterministicShellName,
|
|
22742
|
+
options.downloadBaseUrl
|
|
22743
|
+
) : null);
|
|
22445
22744
|
if (shellAsset) candidates.push({ asset: shellAsset, kind: "shell" });
|
|
22446
22745
|
}
|
|
22447
|
-
const fullAsset = selectDesktopAsset(options.releaseAssets, options.target) ?? (options.directReleaseVersion ? buildGithubReleaseAsset(
|
|
22746
|
+
const fullAsset = selectDesktopAsset(options.releaseAssets, options.target) ?? (options.directReleaseVersion ? buildGithubReleaseAsset(
|
|
22747
|
+
options.repo,
|
|
22748
|
+
options.tag,
|
|
22749
|
+
resolveDesktopAssetName(options.directReleaseVersion, options.target),
|
|
22750
|
+
options.downloadBaseUrl
|
|
22751
|
+
) : null);
|
|
22448
22752
|
if (fullAsset) candidates.push({ asset: fullAsset, kind: "full" });
|
|
22449
22753
|
return candidates;
|
|
22450
22754
|
}
|
|
@@ -22488,8 +22792,35 @@ function githubApiHeaders() {
|
|
|
22488
22792
|
};
|
|
22489
22793
|
}
|
|
22490
22794
|
var GITHUB_API_TIMEOUT_MS = 15e3;
|
|
22491
|
-
|
|
22492
|
-
|
|
22795
|
+
function resolveDesktopSmokeReleaseBaseUrls(env = process.env) {
|
|
22796
|
+
if (env.RUDDER_DESKTOP_SMOKE_AUTO_UPDATE_PUBLIC !== "1") {
|
|
22797
|
+
return {
|
|
22798
|
+
apiBaseUrl: DEFAULT_GITHUB_API_BASE_URL,
|
|
22799
|
+
downloadBaseUrl: DEFAULT_GITHUB_DOWNLOAD_BASE_URL
|
|
22800
|
+
};
|
|
22801
|
+
}
|
|
22802
|
+
const normalizeBaseUrl = (value, fallback) => {
|
|
22803
|
+
const configured = value?.trim();
|
|
22804
|
+
if (!configured) return fallback;
|
|
22805
|
+
const parsed = new URL(configured);
|
|
22806
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
22807
|
+
throw new Error(`Desktop smoke release base URL must use HTTP or HTTPS: ${configured}`);
|
|
22808
|
+
}
|
|
22809
|
+
return configured.replace(/\/+$/u, "");
|
|
22810
|
+
};
|
|
22811
|
+
return {
|
|
22812
|
+
apiBaseUrl: normalizeBaseUrl(
|
|
22813
|
+
env.RUDDER_DESKTOP_SMOKE_RELEASE_API_BASE_URL,
|
|
22814
|
+
DEFAULT_GITHUB_API_BASE_URL
|
|
22815
|
+
),
|
|
22816
|
+
downloadBaseUrl: normalizeBaseUrl(
|
|
22817
|
+
env.RUDDER_DESKTOP_SMOKE_RELEASE_DOWNLOAD_BASE_URL,
|
|
22818
|
+
DEFAULT_GITHUB_DOWNLOAD_BASE_URL
|
|
22819
|
+
)
|
|
22820
|
+
};
|
|
22821
|
+
}
|
|
22822
|
+
async function fetchGithubRelease(repo, tag, apiBaseUrl = DEFAULT_GITHUB_API_BASE_URL) {
|
|
22823
|
+
const endpoint = tag === "latest" ? `${apiBaseUrl}/repos/${repo}/releases/latest` : `${apiBaseUrl}/repos/${repo}/releases/tags/${encodeURIComponent(tag)}`;
|
|
22493
22824
|
const response = await fetchWithTimeout2(endpoint, { headers: githubApiHeaders() }, GITHUB_API_TIMEOUT_MS);
|
|
22494
22825
|
if (!response.ok) {
|
|
22495
22826
|
throw new Error(`GitHub Release ${tag} was not found in ${repo} (${response.status}).`);
|
|
@@ -22517,14 +22848,14 @@ function resolveDesktopShellAssetName(version, target) {
|
|
|
22517
22848
|
function encodeReleaseTagForDownloadUrl2(tag) {
|
|
22518
22849
|
return tag.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
22519
22850
|
}
|
|
22520
|
-
function buildGithubReleaseAssetDownloadUrl(repo, tag, assetName) {
|
|
22851
|
+
function buildGithubReleaseAssetDownloadUrl(repo, tag, assetName, downloadBaseUrl = DEFAULT_GITHUB_DOWNLOAD_BASE_URL) {
|
|
22521
22852
|
const encodedTag = encodeReleaseTagForDownloadUrl2(tag);
|
|
22522
|
-
return
|
|
22853
|
+
return `${downloadBaseUrl}/${repo}/releases/download/${encodedTag}/${encodeURIComponent(assetName)}`;
|
|
22523
22854
|
}
|
|
22524
|
-
function buildGithubReleaseAsset(repo, tag, assetName) {
|
|
22855
|
+
function buildGithubReleaseAsset(repo, tag, assetName, downloadBaseUrl) {
|
|
22525
22856
|
return {
|
|
22526
22857
|
name: assetName,
|
|
22527
|
-
browser_download_url: buildGithubReleaseAssetDownloadUrl(repo, tag, assetName)
|
|
22858
|
+
browser_download_url: buildGithubReleaseAssetDownloadUrl(repo, tag, assetName, downloadBaseUrl)
|
|
22528
22859
|
};
|
|
22529
22860
|
}
|
|
22530
22861
|
function checksumForFile(filePath) {
|
|
@@ -22533,16 +22864,16 @@ function checksumForFile(filePath) {
|
|
|
22533
22864
|
return hash.digest("hex");
|
|
22534
22865
|
}
|
|
22535
22866
|
function resolveAssetChecksum(checksums, assetName) {
|
|
22536
|
-
const expected = checksums.get(
|
|
22867
|
+
const expected = checksums.get(path23.basename(assetName));
|
|
22537
22868
|
if (!expected) {
|
|
22538
|
-
throw new Error(`Desktop release checksums do not include ${
|
|
22869
|
+
throw new Error(`Desktop release checksums do not include ${path23.basename(assetName)}.`);
|
|
22539
22870
|
}
|
|
22540
22871
|
return expected;
|
|
22541
22872
|
}
|
|
22542
22873
|
function assertChecksumMatch(filePath, expected) {
|
|
22543
22874
|
const actual = checksumForFile(filePath);
|
|
22544
22875
|
if (actual !== expected.toLowerCase()) {
|
|
22545
|
-
throw new Error(`Checksum mismatch for ${
|
|
22876
|
+
throw new Error(`Checksum mismatch for ${path23.basename(filePath)}.`);
|
|
22546
22877
|
}
|
|
22547
22878
|
return actual;
|
|
22548
22879
|
}
|
|
@@ -22561,10 +22892,10 @@ function normalizeDesktopAssetChecksum(checksum) {
|
|
|
22561
22892
|
return normalized;
|
|
22562
22893
|
}
|
|
22563
22894
|
function resolveDesktopAssetCacheDir(assetChecksum, homeDir = resolveRudderHomeDir()) {
|
|
22564
|
-
return
|
|
22895
|
+
return path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR, normalizeDesktopAssetChecksum(assetChecksum));
|
|
22565
22896
|
}
|
|
22566
22897
|
function resolveDesktopCachedAssetPath(assetName, assetChecksum, homeDir = resolveRudderHomeDir()) {
|
|
22567
|
-
return
|
|
22898
|
+
return path23.join(resolveDesktopAssetCacheDir(assetChecksum, homeDir), path23.basename(assetName));
|
|
22568
22899
|
}
|
|
22569
22900
|
async function pruneDesktopAssetCache(options = {}) {
|
|
22570
22901
|
const homeDir = options.homeDir ?? resolveRudderHomeDir();
|
|
@@ -22610,7 +22941,7 @@ async function maybePruneDesktopAssetCache(options) {
|
|
|
22610
22941
|
return result.deleted.length > 0 || result.warnings.length > 0 ? result : null;
|
|
22611
22942
|
}
|
|
22612
22943
|
async function scanDesktopAssetCacheEntries(homeDir) {
|
|
22613
|
-
const cacheRoot =
|
|
22944
|
+
const cacheRoot = path23.join(homeDir, DESKTOP_ASSET_CACHE_DIR);
|
|
22614
22945
|
const dirents = await readdir3(cacheRoot, { withFileTypes: true }).catch(() => null);
|
|
22615
22946
|
if (!dirents) return [];
|
|
22616
22947
|
const entries = [];
|
|
@@ -22622,7 +22953,7 @@ async function scanDesktopAssetCacheEntries(homeDir) {
|
|
|
22622
22953
|
} catch {
|
|
22623
22954
|
continue;
|
|
22624
22955
|
}
|
|
22625
|
-
const cacheDir =
|
|
22956
|
+
const cacheDir = path23.join(cacheRoot, dirent.name);
|
|
22626
22957
|
const stats = await desktopCacheDirectoryStats(cacheDir);
|
|
22627
22958
|
entries.push({
|
|
22628
22959
|
cacheDir,
|
|
@@ -22634,7 +22965,7 @@ async function scanDesktopAssetCacheEntries(homeDir) {
|
|
|
22634
22965
|
return entries;
|
|
22635
22966
|
}
|
|
22636
22967
|
async function desktopCacheDirectoryStats(targetPath) {
|
|
22637
|
-
const fallbackStat = await
|
|
22968
|
+
const fallbackStat = await stat4(targetPath).catch(() => null);
|
|
22638
22969
|
const dirents = await readdir3(targetPath, { withFileTypes: true }).catch(() => null);
|
|
22639
22970
|
if (!dirents) {
|
|
22640
22971
|
return {
|
|
@@ -22646,8 +22977,8 @@ async function desktopCacheDirectoryStats(targetPath) {
|
|
|
22646
22977
|
let lastUsedAtMs = Number(fallbackStat?.mtimeMs ?? 0);
|
|
22647
22978
|
for (const dirent of dirents) {
|
|
22648
22979
|
if (dirent.isSymbolicLink()) continue;
|
|
22649
|
-
const entryPath =
|
|
22650
|
-
const entryStat = await
|
|
22980
|
+
const entryPath = path23.join(targetPath, dirent.name);
|
|
22981
|
+
const entryStat = await stat4(entryPath).catch(() => null);
|
|
22651
22982
|
if (!entryStat) continue;
|
|
22652
22983
|
lastUsedAtMs = Math.max(lastUsedAtMs, Number(entryStat.mtimeMs ?? 0));
|
|
22653
22984
|
if (dirent.isDirectory()) {
|
|
@@ -22723,14 +23054,14 @@ async function downloadDesktopAssetWithCache(asset, expectedChecksum, options =
|
|
|
22723
23054
|
await rm3(cachePath, { force: true });
|
|
22724
23055
|
}
|
|
22725
23056
|
}
|
|
22726
|
-
const outputDir = options.outputDir ?? await mkdtemp2(
|
|
23057
|
+
const outputDir = options.outputDir ?? await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
|
|
22727
23058
|
const removeOutputDir = options.outputDir ? false : true;
|
|
22728
23059
|
try {
|
|
22729
23060
|
const downloadedPath = await downloadAsset(asset, outputDir, options.progressFactory, normalizedChecksum);
|
|
22730
23061
|
const checksum = assertChecksumMatch(downloadedPath, normalizedChecksum);
|
|
22731
|
-
await
|
|
22732
|
-
if (
|
|
22733
|
-
await
|
|
23062
|
+
await mkdir3(path23.dirname(cachePath), { recursive: true });
|
|
23063
|
+
if (path23.resolve(downloadedPath) !== path23.resolve(cachePath)) {
|
|
23064
|
+
await copyFile(downloadedPath, cachePath);
|
|
22734
23065
|
}
|
|
22735
23066
|
return { path: cachePath, checksum, cacheStatus: "miss" };
|
|
22736
23067
|
} finally {
|
|
@@ -22746,8 +23077,8 @@ async function pathExists2(targetPath) {
|
|
|
22746
23077
|
}
|
|
22747
23078
|
}
|
|
22748
23079
|
function resolveDesktopInstallLockPath(paths) {
|
|
22749
|
-
const installRootHash = createHash4("sha256").update(
|
|
22750
|
-
return
|
|
23080
|
+
const installRootHash = createHash4("sha256").update(path23.resolve(paths.installRoot)).digest("hex").slice(0, 16);
|
|
23081
|
+
return path23.join(path23.dirname(paths.appPath), `.rudder-desktop-install-${installRootHash}.lock`);
|
|
22751
23082
|
}
|
|
22752
23083
|
async function readDesktopInstallLock(lockPath) {
|
|
22753
23084
|
try {
|
|
@@ -22767,17 +23098,17 @@ async function readDesktopInstallLock(lockPath) {
|
|
|
22767
23098
|
}
|
|
22768
23099
|
async function withDesktopInstallLock(paths, fn, options = {}) {
|
|
22769
23100
|
const lockPath = resolveDesktopInstallLockPath(paths);
|
|
22770
|
-
const lockDir =
|
|
23101
|
+
const lockDir = path23.dirname(lockPath);
|
|
22771
23102
|
const timeoutMs = options.timeoutMs ?? DESKTOP_INSTALL_LOCK_TIMEOUT_MS;
|
|
22772
23103
|
const pollMs = options.pollMs ?? DESKTOP_INSTALL_LOCK_POLL_MS;
|
|
22773
23104
|
const startedAt = Date.now();
|
|
22774
23105
|
const payload = {
|
|
22775
23106
|
lockId: randomUUID(),
|
|
22776
23107
|
pid: process.pid,
|
|
22777
|
-
installRoot:
|
|
23108
|
+
installRoot: path23.resolve(paths.installRoot),
|
|
22778
23109
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22779
23110
|
};
|
|
22780
|
-
await
|
|
23111
|
+
await mkdir3(lockDir, { recursive: true });
|
|
22781
23112
|
while (true) {
|
|
22782
23113
|
try {
|
|
22783
23114
|
await writeFile3(lockPath, `${JSON.stringify(payload, null, 2)}
|
|
@@ -22834,7 +23165,7 @@ function isSuccessfulRobocopyExitCode(status) {
|
|
|
22834
23165
|
}
|
|
22835
23166
|
async function extractZip(zipPath, outputDir, target) {
|
|
22836
23167
|
await rm3(outputDir, { recursive: true, force: true });
|
|
22837
|
-
await
|
|
23168
|
+
await mkdir3(outputDir, { recursive: true });
|
|
22838
23169
|
if (target.platform === "macos") {
|
|
22839
23170
|
runChecked("ditto", ["-x", "-k", zipPath, outputDir]);
|
|
22840
23171
|
return;
|
|
@@ -22850,7 +23181,7 @@ async function findPath(root, predicate, maxDepth = 5) {
|
|
|
22850
23181
|
async function visit(dir, depth) {
|
|
22851
23182
|
const entries = await readdir3(dir, { withFileTypes: true });
|
|
22852
23183
|
for (const entry of entries) {
|
|
22853
|
-
const fullPath =
|
|
23184
|
+
const fullPath = path23.join(dir, entry.name);
|
|
22854
23185
|
if (predicate(fullPath, entry.isDirectory())) return fullPath;
|
|
22855
23186
|
if (entry.isDirectory() && depth < maxDepth) {
|
|
22856
23187
|
const nested = await visit(fullPath, depth + 1);
|
|
@@ -22862,18 +23193,18 @@ async function findPath(root, predicate, maxDepth = 5) {
|
|
|
22862
23193
|
return await visit(root, 0);
|
|
22863
23194
|
}
|
|
22864
23195
|
async function findMacApp(extractDir) {
|
|
22865
|
-
const direct =
|
|
23196
|
+
const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.app`);
|
|
22866
23197
|
if (await pathExists2(direct)) return direct;
|
|
22867
|
-
const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory &&
|
|
23198
|
+
const found = await findPath(extractDir, (filePath, isDirectory) => isDirectory && path23.basename(filePath) === `${DESKTOP_APP_NAME}.app`);
|
|
22868
23199
|
if (!found) throw new Error(`Portable macOS archive did not contain ${DESKTOP_APP_NAME}.app.`);
|
|
22869
23200
|
return found;
|
|
22870
23201
|
}
|
|
22871
23202
|
async function findWindowsAppDir(extractDir) {
|
|
22872
|
-
const direct =
|
|
23203
|
+
const direct = path23.join(extractDir, `${DESKTOP_APP_NAME}.exe`);
|
|
22873
23204
|
if (await pathExists2(direct)) return extractDir;
|
|
22874
|
-
const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory &&
|
|
23205
|
+
const executable = await findPath(extractDir, (filePath, isDirectory) => !isDirectory && path23.basename(filePath).toLowerCase() === `${DESKTOP_APP_NAME.toLowerCase()}.exe`);
|
|
22875
23206
|
if (!executable) throw new Error(`Portable Windows archive did not contain ${DESKTOP_APP_NAME}.exe.`);
|
|
22876
|
-
return
|
|
23207
|
+
return path23.dirname(executable);
|
|
22877
23208
|
}
|
|
22878
23209
|
async function readInstallMetadata(metadataPath) {
|
|
22879
23210
|
try {
|
|
@@ -22941,7 +23272,7 @@ async function waitForUpdateQuitResponse(responsePath, timeoutMs = 8e3) {
|
|
|
22941
23272
|
}
|
|
22942
23273
|
async function requestDesktopQuit(executablePath, target, options = {}) {
|
|
22943
23274
|
if (!await pathExists2(executablePath)) return { ok: true, status: "not_running" };
|
|
22944
|
-
const responsePath =
|
|
23275
|
+
const responsePath = path23.join(tmpdir(), `rudder-update-quit-${process.pid}-${Date.now()}.json`);
|
|
22945
23276
|
const result = spawnSync4(executablePath, [
|
|
22946
23277
|
`${DESKTOP_UPDATE_QUIT_ARG}=${responsePath}`,
|
|
22947
23278
|
...options.forceUpdate ? [DESKTOP_UPDATE_FORCE_ARG] : []
|
|
@@ -23099,13 +23430,13 @@ async function prepareForDesktopReplace(paths, target, options = {}) {
|
|
|
23099
23430
|
throw new Error(`Failed to replace existing Rudder Desktop at ${replacePath}. Close Rudder and rerun start.`);
|
|
23100
23431
|
}
|
|
23101
23432
|
async function installPortableDesktop(installerPath, paths, target) {
|
|
23102
|
-
await
|
|
23433
|
+
await mkdir3(paths.installRoot, { recursive: true });
|
|
23103
23434
|
if (target.platform === "linux") {
|
|
23104
|
-
await
|
|
23105
|
-
await
|
|
23435
|
+
await copyFile(installerPath, paths.appPath);
|
|
23436
|
+
await chmod2(paths.appPath, 493);
|
|
23106
23437
|
return;
|
|
23107
23438
|
}
|
|
23108
|
-
const extractDir = await mkdtemp2(
|
|
23439
|
+
const extractDir = await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-extract."));
|
|
23109
23440
|
try {
|
|
23110
23441
|
await extractZip(installerPath, extractDir, target);
|
|
23111
23442
|
if (target.platform === "macos") {
|
|
@@ -23114,7 +23445,7 @@ async function installPortableDesktop(installerPath, paths, target) {
|
|
|
23114
23445
|
return;
|
|
23115
23446
|
}
|
|
23116
23447
|
const appSource = await findWindowsAppDir(extractDir);
|
|
23117
|
-
await
|
|
23448
|
+
await mkdir3(path23.dirname(paths.installRoot), { recursive: true });
|
|
23118
23449
|
await copyPortableAppBundle(appSource, paths.installRoot);
|
|
23119
23450
|
} finally {
|
|
23120
23451
|
await rm3(extractDir, { recursive: true, force: true });
|
|
@@ -23122,7 +23453,7 @@ async function installPortableDesktop(installerPath, paths, target) {
|
|
|
23122
23453
|
}
|
|
23123
23454
|
async function copyPortableAppBundle(sourcePath, destinationPath) {
|
|
23124
23455
|
if (process.platform === "win32") {
|
|
23125
|
-
await
|
|
23456
|
+
await mkdir3(destinationPath, { recursive: true });
|
|
23126
23457
|
const command = buildWindowsRobocopyMirrorCommand(sourcePath, destinationPath);
|
|
23127
23458
|
const result = spawnSync4(command.command, command.args, {
|
|
23128
23459
|
encoding: "utf8",
|
|
@@ -23131,7 +23462,7 @@ async function copyPortableAppBundle(sourcePath, destinationPath) {
|
|
|
23131
23462
|
if (isSuccessfulRobocopyExitCode(result.status)) return;
|
|
23132
23463
|
throw new Error(formatCommandFailure(command.command, command.args, result.stdout, result.stderr));
|
|
23133
23464
|
}
|
|
23134
|
-
await
|
|
23465
|
+
await cp(sourcePath, destinationPath, { recursive: true, verbatimSymlinks: true });
|
|
23135
23466
|
}
|
|
23136
23467
|
async function removeMacQuarantine(paths, target) {
|
|
23137
23468
|
if (target.platform !== "macos") return;
|
|
@@ -23155,26 +23486,26 @@ function buildLinuxDesktopEntry(executablePath) {
|
|
|
23155
23486
|
].join("\n");
|
|
23156
23487
|
}
|
|
23157
23488
|
async function writeLinuxLaunchers(paths) {
|
|
23158
|
-
const desktopDir =
|
|
23159
|
-
await
|
|
23160
|
-
await writeFile3(
|
|
23161
|
-
const binDir =
|
|
23162
|
-
await
|
|
23163
|
-
const wrapperPath =
|
|
23489
|
+
const desktopDir = path23.join(homedir(), ".local", "share", "applications");
|
|
23490
|
+
await mkdir3(desktopDir, { recursive: true });
|
|
23491
|
+
await writeFile3(path23.join(desktopDir, "rudder.desktop"), buildLinuxDesktopEntry(paths.executablePath), "utf8");
|
|
23492
|
+
const binDir = path23.join(homedir(), ".local", "bin");
|
|
23493
|
+
await mkdir3(binDir, { recursive: true });
|
|
23494
|
+
const wrapperPath = path23.join(binDir, "rudder-desktop");
|
|
23164
23495
|
const escaped = paths.executablePath.replaceAll("'", `'"'"'`);
|
|
23165
23496
|
await writeFile3(wrapperPath, `#!/bin/sh
|
|
23166
23497
|
exec '${escaped}' "$@"
|
|
23167
23498
|
`, "utf8");
|
|
23168
|
-
await
|
|
23499
|
+
await chmod2(wrapperPath, 493);
|
|
23169
23500
|
}
|
|
23170
23501
|
function buildWindowsShortcutScript(executablePath) {
|
|
23171
|
-
const appData = process.env.APPDATA?.trim() ||
|
|
23172
|
-
const shortcutPath =
|
|
23502
|
+
const appData = process.env.APPDATA?.trim() || path23.join(homedir(), "AppData", "Roaming");
|
|
23503
|
+
const shortcutPath = path23.join(appData, "Microsoft", "Windows", "Start Menu", "Programs", "Rudder.lnk");
|
|
23173
23504
|
return [
|
|
23174
23505
|
"$shell = New-Object -ComObject WScript.Shell",
|
|
23175
23506
|
`$shortcut = $shell.CreateShortcut(${powershellQuote(shortcutPath)})`,
|
|
23176
23507
|
`$shortcut.TargetPath = ${powershellQuote(executablePath)}`,
|
|
23177
|
-
`$shortcut.WorkingDirectory = ${powershellQuote(
|
|
23508
|
+
`$shortcut.WorkingDirectory = ${powershellQuote(path23.dirname(executablePath))}`,
|
|
23178
23509
|
"$shortcut.Save()"
|
|
23179
23510
|
].join("; ");
|
|
23180
23511
|
}
|
|
@@ -23206,7 +23537,7 @@ function launchDesktop(paths, target) {
|
|
|
23206
23537
|
spawn3(paths.executablePath, [], { detached: true, stdio: "ignore" }).unref();
|
|
23207
23538
|
}
|
|
23208
23539
|
async function writeInstallMetadata(paths, releaseTag, assetName, assetChecksum, assetKind = "full") {
|
|
23209
|
-
mkdirSync2(
|
|
23540
|
+
mkdirSync2(path23.dirname(paths.metadataPath), { recursive: true });
|
|
23210
23541
|
const metadata = {
|
|
23211
23542
|
version: 1,
|
|
23212
23543
|
releaseTag,
|
|
@@ -23253,6 +23584,7 @@ async function startCommand(opts) {
|
|
|
23253
23584
|
const version = opts.targetVersion?.trim() || opts.version?.trim() || resolveCurrentCliVersion();
|
|
23254
23585
|
const dryRun = opts.dryRun === true;
|
|
23255
23586
|
const desktopProgressJson = opts.desktopProgressJson === true;
|
|
23587
|
+
const desktopRuntimeBestEffort = opts.desktopRuntimeBestEffort === true && installDesktop;
|
|
23256
23588
|
const exactDesktopAssetPath = opts.desktopAssetPath?.trim() || null;
|
|
23257
23589
|
const exactDesktopAssetChecksum = opts.desktopAssetChecksum?.trim() || null;
|
|
23258
23590
|
const exactDesktopAssetName = opts.desktopAssetName?.trim() || null;
|
|
@@ -23261,7 +23593,7 @@ async function startCommand(opts) {
|
|
|
23261
23593
|
if (!exactDesktopAssetPath || !exactDesktopAssetChecksum || !exactDesktopAssetName || !exactDesktopReleaseDigest) {
|
|
23262
23594
|
throw new Error("Exact Desktop asset mode requires path, checksum, asset name, and release digest.");
|
|
23263
23595
|
}
|
|
23264
|
-
if (!
|
|
23596
|
+
if (!path23.isAbsolute(exactDesktopAssetPath) || exactDesktopAssetName.includes("/") || !/^[a-f0-9]{64}$/iu.test(exactDesktopAssetChecksum) || !/^[a-f0-9]{64}$/iu.test(exactDesktopReleaseDigest)) {
|
|
23265
23597
|
throw new Error("Exact Desktop asset mode received invalid candidate identity.");
|
|
23266
23598
|
}
|
|
23267
23599
|
if (opts.desktopAssetKind && opts.desktopAssetKind !== "full" && opts.desktopAssetKind !== "shell") {
|
|
@@ -23284,13 +23616,29 @@ async function startCommand(opts) {
|
|
|
23284
23616
|
}
|
|
23285
23617
|
if (installRuntime) {
|
|
23286
23618
|
p15.log.step("Preparing Rudder runtime");
|
|
23619
|
+
if (desktopProgressJson && desktopRuntimeBestEffort) {
|
|
23620
|
+
writeDesktopProgress({
|
|
23621
|
+
phase: "preparing_runtime",
|
|
23622
|
+
message: "Preparing the lightweight Desktop update runtime; the full package will be used if this takes too long."
|
|
23623
|
+
});
|
|
23624
|
+
await waitForDesktopRuntimeSmokeEvidence("RUDDER_DESKTOP_SMOKE_RUNTIME_PREPARING_DELAY_MS");
|
|
23625
|
+
}
|
|
23287
23626
|
if (dryRun) {
|
|
23288
23627
|
p15.log.message(`[dry-run] Would install or reuse ${pc14.cyan(`@rudderhq/server@${version}`)} in the Rudder runtime cache.`);
|
|
23289
23628
|
} else {
|
|
23290
23629
|
const spinner3 = p15.spinner();
|
|
23291
23630
|
spinner3.start("Installing or reusing Rudder runtime...");
|
|
23292
23631
|
try {
|
|
23293
|
-
const runtime = await ensureRuntimeInstalled({
|
|
23632
|
+
const runtime = await ensureRuntimeInstalled({
|
|
23633
|
+
version,
|
|
23634
|
+
preparePostgresPayload: true,
|
|
23635
|
+
...desktopRuntimeBestEffort ? {
|
|
23636
|
+
timeoutMs: DESKTOP_RUNTIME_PREPARE_TIMEOUT_MS,
|
|
23637
|
+
cleanupIncompleteOnFailure: true,
|
|
23638
|
+
pruneRuntimeCache: false,
|
|
23639
|
+
allowLatestFallback: false
|
|
23640
|
+
} : {}
|
|
23641
|
+
});
|
|
23294
23642
|
runtimeSupportsShellAssets = runtimeSupportsDesktopShellAssets(version, runtime);
|
|
23295
23643
|
spinner3.stop(
|
|
23296
23644
|
runtime.status === "hit" ? `Rudder runtime cache hit at ${pc14.cyan(runtime.cacheDir)}.` : `Rudder runtime installed at ${pc14.cyan(runtime.cacheDir)}.`
|
|
@@ -23301,12 +23649,27 @@ async function startCommand(opts) {
|
|
|
23301
23649
|
if (!runtimeSupportsShellAssets && installDesktop) {
|
|
23302
23650
|
p15.log.warn("Rudder runtime did not resolve to the exact Desktop version; the full portable Desktop asset will be used.");
|
|
23303
23651
|
}
|
|
23652
|
+
if (desktopProgressJson && desktopRuntimeBestEffort) {
|
|
23653
|
+
writeDesktopProgress({
|
|
23654
|
+
phase: "preparing_runtime",
|
|
23655
|
+
message: runtimeSupportsShellAssets ? "Lightweight Desktop update runtime is ready." : "Lightweight runtime is unavailable; continuing with the full Desktop package."
|
|
23656
|
+
});
|
|
23657
|
+
}
|
|
23304
23658
|
} catch (error) {
|
|
23305
23659
|
spinner3.stop(pc14.red("Rudder runtime installation failed."));
|
|
23306
23660
|
if (error instanceof RuntimeInstallError && error.output) {
|
|
23307
23661
|
p15.log.message(pc14.dim(error.output));
|
|
23308
23662
|
}
|
|
23309
|
-
throw error;
|
|
23663
|
+
if (!desktopRuntimeBestEffort) throw error;
|
|
23664
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
23665
|
+
p15.log.warn(`Lightweight Desktop update runtime preparation failed; continuing with the full portable asset. ${detail}`);
|
|
23666
|
+
if (desktopProgressJson) {
|
|
23667
|
+
writeDesktopProgress({
|
|
23668
|
+
phase: "preparing_runtime",
|
|
23669
|
+
message: "Lightweight runtime preparation did not finish; continuing with the full Desktop package."
|
|
23670
|
+
});
|
|
23671
|
+
await waitForDesktopRuntimeSmokeEvidence("RUDDER_DESKTOP_SMOKE_RUNTIME_FALLBACK_DELAY_MS");
|
|
23672
|
+
}
|
|
23310
23673
|
}
|
|
23311
23674
|
}
|
|
23312
23675
|
}
|
|
@@ -23341,11 +23704,12 @@ async function startCommand(opts) {
|
|
|
23341
23704
|
if (installDesktop) {
|
|
23342
23705
|
const downloadSource = resolveDesktopDownloadSource(opts.downloadSource);
|
|
23343
23706
|
const mirrorBaseUrl = resolveDesktopReleaseMirrorBaseUrl(repo);
|
|
23707
|
+
const smokeReleaseBaseUrls = resolveDesktopSmokeReleaseBaseUrls();
|
|
23344
23708
|
const target = resolveDesktopAssetTarget();
|
|
23345
23709
|
const tag = resolveDesktopReleaseTag(version);
|
|
23346
|
-
const installRoot = opts.desktopInstallDir ?
|
|
23710
|
+
const installRoot = opts.desktopInstallDir ? path23.resolve(opts.desktopInstallDir) : resolveDefaultDesktopInstallRoot(target);
|
|
23347
23711
|
const installPaths = resolveDesktopInstallPaths(target, installRoot);
|
|
23348
|
-
const outputDir = opts.outputDir ?
|
|
23712
|
+
const outputDir = opts.outputDir ? path23.resolve(opts.outputDir) : await mkdtemp2(path23.join(tmpdir(), "rudder-desktop-installer."));
|
|
23349
23713
|
p15.log.step("Installing desktop app");
|
|
23350
23714
|
p15.log.message(`Release: ${pc14.cyan(`${repo}@${tag}`)}`);
|
|
23351
23715
|
p15.log.message(`Target: ${pc14.cyan(`${target.platform}/${target.arch}`)}`);
|
|
@@ -23385,13 +23749,13 @@ async function startCommand(opts) {
|
|
|
23385
23749
|
if (computedReleaseDigest !== exactDesktopReleaseDigest.toLowerCase()) {
|
|
23386
23750
|
throw new Error("Exact Desktop asset release digest does not match the candidate identity.");
|
|
23387
23751
|
}
|
|
23388
|
-
const descriptor = await
|
|
23752
|
+
const descriptor = await stat4(exactDesktopAssetPath);
|
|
23389
23753
|
if (!descriptor.isFile()) throw new Error("Exact Desktop asset must be a regular file.");
|
|
23390
23754
|
const linkDescriptor = await lstat(exactDesktopAssetPath);
|
|
23391
23755
|
if (linkDescriptor.isSymbolicLink()) throw new Error("Exact Desktop asset must not be a symbolic link.");
|
|
23392
23756
|
const checksum = await runStartPhase(
|
|
23393
23757
|
"Verifying staged Desktop checksum...",
|
|
23394
|
-
`Verified ${pc14.cyan(
|
|
23758
|
+
`Verified ${pc14.cyan(path23.basename(exactDesktopAssetPath))}.`,
|
|
23395
23759
|
() => assertChecksumMatch(exactDesktopAssetPath, expectedChecksum),
|
|
23396
23760
|
desktopProgressJson ? "verifying_checksum" : null
|
|
23397
23761
|
);
|
|
@@ -23403,7 +23767,7 @@ async function startCommand(opts) {
|
|
|
23403
23767
|
release = await runStartPhase(
|
|
23404
23768
|
"Resolving Desktop release...",
|
|
23405
23769
|
"Desktop release resolved.",
|
|
23406
|
-
() => fetchGithubRelease(repo, tag),
|
|
23770
|
+
() => fetchGithubRelease(repo, tag, smokeReleaseBaseUrls.apiBaseUrl),
|
|
23407
23771
|
desktopProgressJson ? "resolving_release" : null
|
|
23408
23772
|
);
|
|
23409
23773
|
} catch (error) {
|
|
@@ -23420,12 +23784,18 @@ async function startCommand(opts) {
|
|
|
23420
23784
|
repo,
|
|
23421
23785
|
tag,
|
|
23422
23786
|
directReleaseVersion,
|
|
23423
|
-
allowShellAssets: runtimeSupportsShellAssets
|
|
23787
|
+
allowShellAssets: runtimeSupportsShellAssets,
|
|
23788
|
+
downloadBaseUrl: smokeReleaseBaseUrls.downloadBaseUrl
|
|
23424
23789
|
});
|
|
23425
23790
|
if (assetCandidates.length === 0) {
|
|
23426
23791
|
throw new Error(`No Rudder Desktop portable asset found for ${target.platform}/${target.arch} in ${repo}@${releaseTag}.`);
|
|
23427
23792
|
}
|
|
23428
|
-
const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(
|
|
23793
|
+
const checksumAsset = selectChecksumAsset(release?.assets ?? []) ?? (directReleaseVersion ? buildGithubReleaseAsset(
|
|
23794
|
+
repo,
|
|
23795
|
+
tag,
|
|
23796
|
+
DESKTOP_CHECKSUM_ASSET_NAME,
|
|
23797
|
+
smokeReleaseBaseUrls.downloadBaseUrl
|
|
23798
|
+
) : null);
|
|
23429
23799
|
if (!checksumAsset) {
|
|
23430
23800
|
throw new Error("Desktop release is missing SHASUMS256.txt.");
|
|
23431
23801
|
}
|
|
@@ -23504,7 +23874,7 @@ async function startCommand(opts) {
|
|
|
23504
23874
|
}
|
|
23505
23875
|
const checksum = await runStartPhase(
|
|
23506
23876
|
"Verifying Desktop checksum...",
|
|
23507
|
-
`Verified ${pc14.cyan(
|
|
23877
|
+
`Verified ${pc14.cyan(path23.basename(verifiedAsset.path))}.`,
|
|
23508
23878
|
() => assertChecksumMatch(verifiedAsset.path, expectedChecksum),
|
|
23509
23879
|
desktopProgressJson ? "verifying_checksum" : null
|
|
23510
23880
|
);
|
|
@@ -23515,7 +23885,8 @@ async function startCommand(opts) {
|
|
|
23515
23885
|
percent: 100,
|
|
23516
23886
|
assetName: selectedAsset.name,
|
|
23517
23887
|
assetChecksum: checksum,
|
|
23518
|
-
|
|
23888
|
+
assetKind: selectedAssetKind,
|
|
23889
|
+
stagedArtifactPath: path23.resolve(verifiedAsset.path),
|
|
23519
23890
|
stagedArtifactDigest: checksum,
|
|
23520
23891
|
releaseDigest: createHash4("sha256").update(JSON.stringify({
|
|
23521
23892
|
releaseTag,
|
|
@@ -23608,11 +23979,11 @@ async function startCommand(opts) {
|
|
|
23608
23979
|
|
|
23609
23980
|
// src/config/data-dir.ts
|
|
23610
23981
|
init_home();
|
|
23611
|
-
import
|
|
23982
|
+
import path24 from "node:path";
|
|
23612
23983
|
function applyDataDirOverride(options, support = {}) {
|
|
23613
23984
|
const rawDataDir = options.dataDir?.trim();
|
|
23614
23985
|
if (!rawDataDir) return null;
|
|
23615
|
-
const resolvedDataDir =
|
|
23986
|
+
const resolvedDataDir = path24.resolve(expandHomePrefix(rawDataDir));
|
|
23616
23987
|
process.env.RUDDER_HOME = resolvedDataDir;
|
|
23617
23988
|
if (support.hasConfigOption) {
|
|
23618
23989
|
const hasConfigOverride = Boolean(options.config?.trim()) || Boolean(process.env.RUDDER_CONFIG?.trim());
|
|
@@ -23672,7 +24043,7 @@ function createProgram() {
|
|
|
23672
24043
|
});
|
|
23673
24044
|
loadRudderEnvFile(options.config);
|
|
23674
24045
|
});
|
|
23675
|
-
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);
|
|
24046
|
+
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").addOption(new Option("--desktop-runtime-best-effort").hideHelp()).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);
|
|
23676
24047
|
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);
|
|
23677
24048
|
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) => {
|
|
23678
24049
|
await doctor(opts);
|