@usepipr/runtime 0.3.3 → 0.3.5
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/{commands-4n3t4t5P.d.mts → commands-C6wfO-y7.d.mts} +9 -7
- package/dist/commands-C6wfO-y7.d.mts.map +1 -0
- package/dist/{commands-C1sVsbEj.mjs → commands-DCpIxJkz.mjs} +353 -129
- package/dist/commands-DCpIxJkz.mjs.map +1 -0
- package/dist/index.d.mts +1 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +1 -1
- package/dist/internal/testing.d.mts +7 -4
- package/dist/internal/testing.d.mts.map +1 -1
- package/dist/internal/testing.mjs +2 -2
- package/dist/pi/runtime-tools-extension.d.mts.map +1 -1
- package/dist/pi/runtime-tools-extension.mjs +2 -1
- package/dist/pi/runtime-tools-extension.mjs.map +1 -1
- package/package.json +3 -3
- package/dist/commands-4n3t4t5P.d.mts.map +0 -1
- package/dist/commands-C1sVsbEj.mjs.map +0 -1
|
@@ -13,17 +13,74 @@ import { Buffer as Buffer$1 } from "node:buffer";
|
|
|
13
13
|
import { spawn } from "node:child_process";
|
|
14
14
|
import { Octokit } from "@octokit/rest";
|
|
15
15
|
import { existsSync, mkdirSync } from "node:fs";
|
|
16
|
+
//#region src/config/package-manifest.ts
|
|
17
|
+
function normalizePackageManifest(value) {
|
|
18
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return {};
|
|
19
|
+
const manifest = {};
|
|
20
|
+
const rawManifest = value;
|
|
21
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
22
|
+
const dependencyMap = rawManifest[key];
|
|
23
|
+
if (dependencyMap === null || typeof dependencyMap !== "object" || Array.isArray(dependencyMap)) continue;
|
|
24
|
+
const entries = Object.entries(dependencyMap).filter((entry) => typeof entry[1] === "string");
|
|
25
|
+
if (entries.length > 0) manifest[key] = Object.fromEntries(entries);
|
|
26
|
+
}
|
|
27
|
+
return manifest;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/config/scaffold-versions.ts
|
|
31
|
+
const defaultTypesBunVersion = "1.3.14";
|
|
32
|
+
const defaultTypescriptVersion = "6.0.3";
|
|
33
|
+
const defaultScaffoldTypescriptSpec = `typescript@${defaultTypescriptVersion}`;
|
|
34
|
+
//#endregion
|
|
16
35
|
//#region src/config/config-deps.ts
|
|
17
|
-
const runtimeProvidedPackages = new Set(["@usepipr/sdk", "@types/bun"]);
|
|
36
|
+
const runtimeProvidedPackages = /* @__PURE__ */ new Set(["@usepipr/sdk", "@types/bun"]);
|
|
18
37
|
async function installConfigDependencies(configDir, options = {}) {
|
|
19
38
|
const packageJsonPath = path.join(configDir, "package.json");
|
|
20
|
-
if (!await
|
|
21
|
-
|
|
39
|
+
if (!await Bun.file(packageJsonPath).exists()) return;
|
|
40
|
+
const originalPackageJson = await Bun.file(packageJsonPath).text();
|
|
41
|
+
const manifest = normalizePackageManifest(JSON.parse(originalPackageJson));
|
|
42
|
+
const installablePackages = installableDependencySpecs(manifest);
|
|
43
|
+
if (installablePackages.length === 0) return;
|
|
22
44
|
const bunLockPath = path.join(configDir, "bun.lock");
|
|
23
|
-
|
|
45
|
+
const originalBunLock = await Bun.file(bunLockPath).exists() ? await Bun.file(bunLockPath).text() : void 0;
|
|
46
|
+
if (options.frozen && originalBunLock === void 0) throw new Error(`${configDir}: bun.lock is required when .pipr/package.json declares dependencies. Run \`bun install\` in .pipr/ and commit bun.lock.`);
|
|
24
47
|
await assertBunAvailable();
|
|
25
|
-
const args =
|
|
26
|
-
|
|
48
|
+
const args = configInstallArgs(installablePackages, { frozen: options.frozen === true && !hasRuntimeProvidedDependencies(manifest) });
|
|
49
|
+
await withSanitizedConfigInstallInputs({
|
|
50
|
+
packageJsonPath,
|
|
51
|
+
originalPackageJson,
|
|
52
|
+
bunLockPath,
|
|
53
|
+
originalBunLock
|
|
54
|
+
}, () => runConfigBunInstall(configDir, args));
|
|
55
|
+
}
|
|
56
|
+
function configInstallArgs(installablePackages, options) {
|
|
57
|
+
return [
|
|
58
|
+
"install",
|
|
59
|
+
"--ignore-scripts",
|
|
60
|
+
"--no-save",
|
|
61
|
+
...options.frozen ? ["--frozen-lockfile"] : [],
|
|
62
|
+
...isDefaultScaffoldTypescriptInstall(installablePackages) ? ["--no-verify"] : [],
|
|
63
|
+
...installablePackages.map((dependency) => dependency.spec)
|
|
64
|
+
];
|
|
65
|
+
}
|
|
66
|
+
function isDefaultScaffoldTypescriptInstall(installablePackages) {
|
|
67
|
+
return installablePackages.length === 1 && installablePackages[0]?.spec === defaultScaffoldTypescriptSpec;
|
|
68
|
+
}
|
|
69
|
+
async function withSanitizedConfigInstallInputs(inputs, install) {
|
|
70
|
+
let wroteInstallInputs = false;
|
|
71
|
+
try {
|
|
72
|
+
await Bun.write(inputs.packageJsonPath, sanitizedPackageJsonForConfigInstall(inputs.originalPackageJson));
|
|
73
|
+
wroteInstallInputs = true;
|
|
74
|
+
if (inputs.originalBunLock !== void 0) await Bun.write(inputs.bunLockPath, sanitizedBunLockForConfigInstall(inputs.originalBunLock));
|
|
75
|
+
await install();
|
|
76
|
+
} finally {
|
|
77
|
+
if (wroteInstallInputs) {
|
|
78
|
+
await Bun.write(inputs.packageJsonPath, inputs.originalPackageJson);
|
|
79
|
+
if (inputs.originalBunLock !== void 0) await Bun.write(inputs.bunLockPath, inputs.originalBunLock);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function runConfigBunInstall(configDir, args) {
|
|
27
84
|
const proc = Bun.spawn(["bun", ...args], {
|
|
28
85
|
cwd: configDir,
|
|
29
86
|
env: process.env,
|
|
@@ -33,9 +90,43 @@ async function installConfigDependencies(configDir, options = {}) {
|
|
|
33
90
|
const [exitCode, stderr] = await Promise.all([proc.exited, new Response(proc.stderr).text()]);
|
|
34
91
|
if (exitCode !== 0) throw new Error(`${configDir}: bun install failed (exit ${exitCode}).` + (stderr.trim().length > 0 ? `\n${stderr.trim()}` : ""));
|
|
35
92
|
}
|
|
36
|
-
function
|
|
37
|
-
|
|
38
|
-
|
|
93
|
+
function installableDependencySpecs(manifest) {
|
|
94
|
+
return dependencyEntries(manifest).filter(([name]) => !runtimeProvidedPackages.has(name)).map(([name, version]) => ({
|
|
95
|
+
name,
|
|
96
|
+
spec: `${name}@${version}`
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
function hasRuntimeProvidedDependencies(manifest) {
|
|
100
|
+
return dependencyEntries(manifest).some(([name]) => runtimeProvidedPackages.has(name));
|
|
101
|
+
}
|
|
102
|
+
function dependencyEntries(manifest) {
|
|
103
|
+
return Object.entries({
|
|
104
|
+
...manifest.dependencies ?? {},
|
|
105
|
+
...manifest.devDependencies ?? {}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function sanitizedPackageJsonForConfigInstall(packageJson) {
|
|
109
|
+
const value = JSON.parse(packageJson);
|
|
110
|
+
for (const key of ["dependencies", "devDependencies"]) {
|
|
111
|
+
const dependencies = value[key];
|
|
112
|
+
if (dependencies === null || typeof dependencies !== "object" || Array.isArray(dependencies)) continue;
|
|
113
|
+
const sanitized = Object.fromEntries(Object.entries(dependencies).filter(([name]) => !runtimeProvidedPackages.has(name)));
|
|
114
|
+
if (Object.keys(sanitized).length === 0) delete value[key];
|
|
115
|
+
else value[key] = sanitized;
|
|
116
|
+
}
|
|
117
|
+
return `${JSON.stringify(value, null, 2)}\n`;
|
|
118
|
+
}
|
|
119
|
+
function sanitizedBunLockForConfigInstall(lockfile) {
|
|
120
|
+
let sanitized = lockfile;
|
|
121
|
+
for (const packageName of runtimeProvidedPackages) {
|
|
122
|
+
const escapedName = escapeRegExp(packageName);
|
|
123
|
+
sanitized = sanitized.replace(new RegExp(`^\\s*"${escapedName}":\\s*"[^"]+",\\r?\\n`, "gm"), "");
|
|
124
|
+
sanitized = sanitized.replace(new RegExp(`^\\s*"${escapedName}":\\s*\\[[^\\n]*\\],\\r?\\n(?:\\r?\\n)?`, "gm"), "");
|
|
125
|
+
}
|
|
126
|
+
return sanitized;
|
|
127
|
+
}
|
|
128
|
+
function escapeRegExp(value) {
|
|
129
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
39
130
|
}
|
|
40
131
|
async function assertBunAvailable() {
|
|
41
132
|
try {
|
|
@@ -48,9 +139,6 @@ async function assertBunAvailable() {
|
|
|
48
139
|
throw new Error("bun is required on PATH to install .pipr/package.json dependencies. Install Bun from https://bun.sh");
|
|
49
140
|
}
|
|
50
141
|
}
|
|
51
|
-
async function fileExists$1(filePath) {
|
|
52
|
-
return await Bun.file(filePath).exists();
|
|
53
|
-
}
|
|
54
142
|
//#endregion
|
|
55
143
|
//#region src/config/paths.ts
|
|
56
144
|
function resolveContainedConfigDir(options) {
|
|
@@ -365,6 +453,8 @@ async function installTypedSdkStub(configDir) {
|
|
|
365
453
|
});
|
|
366
454
|
await mkdir(sdkRoot, { recursive: true });
|
|
367
455
|
await Bun.write(path.join(sdkRoot, "package.json"), JSON.stringify({
|
|
456
|
+
name: "@usepipr/sdk",
|
|
457
|
+
version: "0.0.0-pipr-runtime",
|
|
368
458
|
type: "module",
|
|
369
459
|
types: "./index.d.ts",
|
|
370
460
|
exports: { ".": {
|
|
@@ -425,23 +515,93 @@ const starterTsconfig = `{
|
|
|
425
515
|
}
|
|
426
516
|
`;
|
|
427
517
|
//#endregion
|
|
518
|
+
//#region src/shared/semver.ts
|
|
519
|
+
const stableSemverPattern = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/;
|
|
520
|
+
function compareStableSemver(left, right) {
|
|
521
|
+
const leftParts = stableSemverPattern.exec(left);
|
|
522
|
+
const rightParts = stableSemverPattern.exec(right);
|
|
523
|
+
if (!leftParts || !rightParts) throw new Error(`cannot compare non-stable semver versions: ${left}, ${right}`);
|
|
524
|
+
return Number(leftParts[1]) - Number(rightParts[1]) || Number(leftParts[2]) - Number(rightParts[2]) || Number(leftParts[3]) - Number(rightParts[3]);
|
|
525
|
+
}
|
|
526
|
+
//#endregion
|
|
527
|
+
//#region src/shared/version.ts
|
|
528
|
+
const runtimeVersion = "0.3.5";
|
|
529
|
+
//#endregion
|
|
530
|
+
//#region src/config/version-compat.ts
|
|
531
|
+
async function resolveConfigVersionCompatibility(options) {
|
|
532
|
+
const packageJsonPath = path.join(options.configDirPath, "package.json");
|
|
533
|
+
let manifest;
|
|
534
|
+
try {
|
|
535
|
+
manifest = normalizePackageManifest(await Bun.file(packageJsonPath).json());
|
|
536
|
+
} catch (error) {
|
|
537
|
+
if ((error && typeof error === "object" && "code" in error ? error.code : void 0) === "ENOENT") return {
|
|
538
|
+
kind: "unknown",
|
|
539
|
+
runtimeVersion
|
|
540
|
+
};
|
|
541
|
+
throw error;
|
|
542
|
+
}
|
|
543
|
+
const sdkVersion = manifest.dependencies?.["@usepipr/sdk"] ?? manifest.devDependencies?.["@usepipr/sdk"];
|
|
544
|
+
if (!sdkVersion) return {
|
|
545
|
+
kind: "unknown",
|
|
546
|
+
runtimeVersion
|
|
547
|
+
};
|
|
548
|
+
const packageJsonLabel = options.configDir === "." ? "package.json" : `${options.configDir}/package.json`;
|
|
549
|
+
const bunLockLabel = options.configDir === "." ? "bun.lock" : `${options.configDir}/bun.lock`;
|
|
550
|
+
if (!stableSemverPattern.test(sdkVersion)) return {
|
|
551
|
+
kind: "uncomparable",
|
|
552
|
+
runtimeVersion,
|
|
553
|
+
warning: `${packageJsonLabel} declares @usepipr/sdk as ${JSON.stringify(sdkVersion)}; use an exact version to enable Pipr config version checks.`
|
|
554
|
+
};
|
|
555
|
+
if (!stableSemverPattern.test(runtimeVersion)) return {
|
|
556
|
+
kind: "uncomparable",
|
|
557
|
+
runtimeVersion,
|
|
558
|
+
warning: `This Pipr runtime reports version ${runtimeVersion}; skipping Pipr config version checks.`
|
|
559
|
+
};
|
|
560
|
+
const comparison = compareStableSemver(runtimeVersion, sdkVersion);
|
|
561
|
+
if (comparison === 0) return {
|
|
562
|
+
kind: "matched",
|
|
563
|
+
runtimeVersion,
|
|
564
|
+
configVersion: sdkVersion
|
|
565
|
+
};
|
|
566
|
+
if (comparison > 0) return {
|
|
567
|
+
kind: "runtime-newer",
|
|
568
|
+
runtimeVersion,
|
|
569
|
+
configVersion: sdkVersion,
|
|
570
|
+
warning: `${packageJsonLabel} pins @usepipr/sdk ${sdkVersion}, but this Pipr runtime is ${runtimeVersion}. Run \`pipr init --force\` or update ${packageJsonLabel} and ${bunLockLabel} when ready.`
|
|
571
|
+
};
|
|
572
|
+
throw new Error(`${packageJsonLabel} pins @usepipr/sdk ${sdkVersion}, but this Pipr runtime is ${runtimeVersion}. Upgrade Pipr before running this config.`);
|
|
573
|
+
}
|
|
574
|
+
//#endregion
|
|
428
575
|
//#region src/config/ts-loader.ts
|
|
429
576
|
async function loadTypescriptConfig(options) {
|
|
430
|
-
const { projectDir, relativeConfigDir
|
|
577
|
+
const { projectDir, relativeConfigDir } = resolveContainedConfigDir(options);
|
|
431
578
|
const sourceConfigPath = path.join(projectDir, "config.ts");
|
|
432
|
-
if (!await
|
|
579
|
+
if (!await Bun.file(sourceConfigPath).exists()) throw new Error(`No Pipr config found at ${sourceConfigPath}.\nRun \`pipr init\` to create one, or pass \`--config-dir <dir>\`.`);
|
|
580
|
+
const versionCompatibility = await resolveConfigVersionCompatibility({
|
|
581
|
+
configDirPath: projectDir,
|
|
582
|
+
configDir: relativeConfigDir
|
|
583
|
+
});
|
|
433
584
|
if (options.typecheck) await typecheckTypescriptConfig(path.resolve(options.rootDir), relativeConfigDir);
|
|
434
585
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "pipr-config-"));
|
|
435
586
|
try {
|
|
436
587
|
const tempConfigDir = path.join(tempRoot, relativeConfigDir);
|
|
437
|
-
await
|
|
588
|
+
await cp(projectDir, tempConfigDir, {
|
|
589
|
+
recursive: true,
|
|
590
|
+
errorOnExist: false,
|
|
591
|
+
force: true,
|
|
592
|
+
filter: (source) => {
|
|
593
|
+
const relative = path.relative(projectDir, source);
|
|
594
|
+
return relative !== "node_modules" && !relative.startsWith(`node_modules${path.sep}`);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
438
597
|
await prepareConfigDirectory(tempConfigDir, { frozen: true });
|
|
439
598
|
const factory = (await import(`${pathToFileURL(path.join(tempConfigDir, "config.ts")).href}?pipr=${Date.now()}`)).default;
|
|
440
599
|
if (!isPiprConfigFactory(factory)) throw new Error(`${sourceConfigPath}: default export must be created by definePipr()`);
|
|
441
600
|
return {
|
|
442
601
|
plan: buildPiprPlan(factory),
|
|
443
602
|
source: sourceConfigPath,
|
|
444
|
-
tempRoot
|
|
603
|
+
tempRoot,
|
|
604
|
+
versionCompatibility
|
|
445
605
|
};
|
|
446
606
|
} finally {
|
|
447
607
|
await rm(tempRoot, {
|
|
@@ -465,13 +625,14 @@ async function typecheckTypescriptConfig(rootDir, relativeConfigDir) {
|
|
|
465
625
|
force: true,
|
|
466
626
|
filter: (source) => {
|
|
467
627
|
const relative = path.relative(rootDir, source);
|
|
468
|
-
const
|
|
469
|
-
|
|
628
|
+
const parts = relative.split(path.sep);
|
|
629
|
+
const first = parts[0] ?? "";
|
|
630
|
+
return !ignoredTypecheckRootEntries.has(first) && !parts.includes("node_modules") && relative !== "bun.lock";
|
|
470
631
|
}
|
|
471
632
|
});
|
|
472
633
|
await prepareConfigDirectory(tempConfigDir, { frozen: true });
|
|
473
634
|
const tsconfigPath = path.join(tempConfigDir, "tsconfig.json");
|
|
474
|
-
if (!await
|
|
635
|
+
if (!await Bun.file(tsconfigPath).exists()) {
|
|
475
636
|
await mkdir(tempConfigDir, { recursive: true });
|
|
476
637
|
await Bun.write(tsconfigPath, starterTsconfig);
|
|
477
638
|
}
|
|
@@ -484,25 +645,64 @@ async function typecheckTypescriptConfig(rootDir, relativeConfigDir) {
|
|
|
484
645
|
}
|
|
485
646
|
}
|
|
486
647
|
async function typecheckTypescriptConfigWithApi(configDir, tsconfigPath) {
|
|
487
|
-
const ts = await
|
|
648
|
+
const { ts, packageRoot } = await loadTypescriptForConfig(configDir);
|
|
488
649
|
const config = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
|
|
489
650
|
if (config.error) throw new Error(formatTypeScriptDiagnostics(ts, [config.error], configDir));
|
|
490
651
|
const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, configDir);
|
|
491
652
|
const bundledTypeRoots = [];
|
|
492
|
-
if (!await
|
|
653
|
+
if (!await Bun.file(path.join(configDir, "node_modules", "@types", "bun", "package.json")).exists()) try {
|
|
493
654
|
const require = createRequire(import.meta.url);
|
|
494
655
|
bundledTypeRoots.push(path.dirname(path.dirname(require.resolve("@types/bun/package.json"))));
|
|
495
656
|
} catch {}
|
|
496
657
|
const configPath = path.join(configDir, "config.ts");
|
|
497
|
-
const
|
|
658
|
+
const compilerOptions = {
|
|
498
659
|
...parsed.options,
|
|
499
660
|
skipLibCheck: true,
|
|
500
|
-
typeRoots: [
|
|
501
|
-
types: [
|
|
502
|
-
}
|
|
661
|
+
typeRoots: [.../* @__PURE__ */ new Set([...parsed.options.typeRoots ?? [], ...bundledTypeRoots])],
|
|
662
|
+
types: [.../* @__PURE__ */ new Set([...parsed.options.types ?? [], ...bundledTypeRoots.length ? ["bun"] : []])]
|
|
663
|
+
};
|
|
664
|
+
const compilerHost = await createTypescriptCompilerHost(ts, compilerOptions, packageRoot);
|
|
665
|
+
const program = ts.createProgram([configPath, ...parsed.fileNames], compilerOptions, compilerHost);
|
|
503
666
|
const diagnostics = [...parsed.errors, ...ts.getPreEmitDiagnostics(program)];
|
|
504
667
|
if (diagnostics.length > 0) throw new Error(`TypeScript config check failed for ${path.join(configDir, "config.ts")}:\n` + formatTypeScriptDiagnostics(ts, diagnostics, configDir));
|
|
505
668
|
}
|
|
669
|
+
async function loadTypescriptForConfig(configDir) {
|
|
670
|
+
const localPackageRoot = path.join(configDir, "node_modules", "typescript");
|
|
671
|
+
const localApiPath = path.join(localPackageRoot, "lib", "typescript.js");
|
|
672
|
+
if (await fileExists(localApiPath)) return {
|
|
673
|
+
ts: typescriptApi(await import(pathToFileURL(localApiPath).href)),
|
|
674
|
+
packageRoot: localPackageRoot
|
|
675
|
+
};
|
|
676
|
+
return {
|
|
677
|
+
ts: typescriptApi(await import("typescript")),
|
|
678
|
+
packageRoot: await runtimeTypescriptPackageRoot()
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
function typescriptApi(module) {
|
|
682
|
+
const maybeModule = module;
|
|
683
|
+
if (typeof maybeModule.createProgram === "function") return maybeModule;
|
|
684
|
+
if (typeof maybeModule.default?.createProgram === "function") return maybeModule.default;
|
|
685
|
+
throw new Error("TypeScript module does not expose createProgram");
|
|
686
|
+
}
|
|
687
|
+
async function runtimeTypescriptPackageRoot() {
|
|
688
|
+
try {
|
|
689
|
+
const require = createRequire(import.meta.url);
|
|
690
|
+
const packageRoot = path.dirname(require.resolve("typescript/package.json"));
|
|
691
|
+
return await fileExists(path.join(packageRoot, "lib", "typescript.js")) ? packageRoot : void 0;
|
|
692
|
+
} catch {
|
|
693
|
+
return;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
async function createTypescriptCompilerHost(ts, compilerOptions, packageRoot) {
|
|
697
|
+
const compilerHost = ts.createCompilerHost(compilerOptions, true);
|
|
698
|
+
if (!packageRoot) return compilerHost;
|
|
699
|
+
const libDir = path.join(packageRoot, "lib");
|
|
700
|
+
const defaultLibPath = path.join(libDir, ts.getDefaultLibFileName(compilerOptions));
|
|
701
|
+
if (!await fileExists(defaultLibPath)) return compilerHost;
|
|
702
|
+
compilerHost.getDefaultLibFileName = () => defaultLibPath;
|
|
703
|
+
compilerHost.getDefaultLibLocation = () => libDir;
|
|
704
|
+
return compilerHost;
|
|
705
|
+
}
|
|
506
706
|
function formatTypeScriptDiagnostics(ts, diagnostics, configDir) {
|
|
507
707
|
return ts.formatDiagnostics(diagnostics, {
|
|
508
708
|
getCanonicalFileName: (fileName) => fileName,
|
|
@@ -510,19 +710,7 @@ function formatTypeScriptDiagnostics(ts, diagnostics, configDir) {
|
|
|
510
710
|
getNewLine: () => "\n"
|
|
511
711
|
});
|
|
512
712
|
}
|
|
513
|
-
|
|
514
|
-
await cp(sourceDir, targetDir, {
|
|
515
|
-
recursive: true,
|
|
516
|
-
errorOnExist: false,
|
|
517
|
-
force: true,
|
|
518
|
-
filter: (source) => !isIgnoredConfigCopyPath(source, sourceDir)
|
|
519
|
-
});
|
|
520
|
-
}
|
|
521
|
-
function isIgnoredConfigCopyPath(source, configDir) {
|
|
522
|
-
const relative = path.relative(configDir, source);
|
|
523
|
-
return relative === "node_modules" || relative.startsWith(`node_modules${path.sep}`);
|
|
524
|
-
}
|
|
525
|
-
const ignoredTypecheckRootEntries = new Set([
|
|
713
|
+
const ignoredTypecheckRootEntries = /* @__PURE__ */ new Set([
|
|
526
714
|
".fallow",
|
|
527
715
|
".git",
|
|
528
716
|
".turbo",
|
|
@@ -543,8 +731,10 @@ async function loadRuntimeProject(options) {
|
|
|
543
731
|
settings: planToRuntimeSettings(loaded.plan, {
|
|
544
732
|
source: loaded.source,
|
|
545
733
|
env: options.env,
|
|
546
|
-
requireProviderEnv: options.requireProviderEnv
|
|
547
|
-
|
|
734
|
+
requireProviderEnv: options.requireProviderEnv,
|
|
735
|
+
warnings: [loaded.versionCompatibility.warning].filter((warning) => warning !== void 0)
|
|
736
|
+
}),
|
|
737
|
+
versionCompatibility: loaded.versionCompatibility
|
|
548
738
|
};
|
|
549
739
|
}
|
|
550
740
|
async function validateProject(options) {
|
|
@@ -589,7 +779,7 @@ function planToRuntimeSettings(plan, options) {
|
|
|
589
779
|
},
|
|
590
780
|
limits: plan.limits
|
|
591
781
|
},
|
|
592
|
-
warnings: []
|
|
782
|
+
warnings: options.warnings ?? []
|
|
593
783
|
});
|
|
594
784
|
}
|
|
595
785
|
function normalizeAutoResolveConfig(options, defaultProvider) {
|
|
@@ -2954,9 +3144,8 @@ function isOfficialInitRecipeId(recipe) {
|
|
|
2954
3144
|
//#endregion
|
|
2955
3145
|
//#region src/config/init.ts
|
|
2956
3146
|
const supportedOfficialInitAdapters = ["github"];
|
|
2957
|
-
const defaultWorkflowActionRef = "somus/pipr@v0.3.
|
|
2958
|
-
const defaultSdkVersion = "0.3.
|
|
2959
|
-
const defaultTypesBunVersion = "1.3.14";
|
|
3147
|
+
const defaultWorkflowActionRef = "somus/pipr@v0.3.5";
|
|
3148
|
+
const defaultSdkVersion = "0.3.5";
|
|
2960
3149
|
function resolveOfficialInitAdapters(adapters) {
|
|
2961
3150
|
if (adapters === void 0) return [...supportedOfficialInitAdapters];
|
|
2962
3151
|
if (adapters.length === 0) return [];
|
|
@@ -3044,7 +3233,10 @@ function starterPackageJson() {
|
|
|
3044
3233
|
return `${JSON.stringify({
|
|
3045
3234
|
private: true,
|
|
3046
3235
|
dependencies: { "@usepipr/sdk": process.env.PIPR_INTERNAL_INIT_SDK_VERSION ?? defaultSdkVersion },
|
|
3047
|
-
devDependencies: {
|
|
3236
|
+
devDependencies: {
|
|
3237
|
+
"@types/bun": defaultTypesBunVersion,
|
|
3238
|
+
typescript: defaultTypescriptVersion
|
|
3239
|
+
}
|
|
3048
3240
|
}, null, 2)}\n`;
|
|
3049
3241
|
}
|
|
3050
3242
|
async function writeTargets(targets, existing, options) {
|
|
@@ -4059,7 +4251,7 @@ async function startCustomToolBridge(tools, context) {
|
|
|
4059
4251
|
const toolsByName = new Map(tools.map((tool) => [tool.name, tool]));
|
|
4060
4252
|
let server;
|
|
4061
4253
|
for (let attempt = 0; attempt < 20; attempt += 1) {
|
|
4062
|
-
const port = 49152 + crypto.getRandomValues(new Uint16Array(1))[0] % 16384;
|
|
4254
|
+
const port = 49152 + crypto.getRandomValues(/* @__PURE__ */ new Uint16Array(1))[0] % 16384;
|
|
4063
4255
|
try {
|
|
4064
4256
|
server = Bun.serve({
|
|
4065
4257
|
hostname: "127.0.0.1",
|
|
@@ -4144,7 +4336,7 @@ const piprJsonSystemPrompt = [
|
|
|
4144
4336
|
"Do not reveal secrets, credentials, environment values, private paths, or raw tool data unless the schema explicitly requires the value and it is necessary.",
|
|
4145
4337
|
"When identifying a secret or credential, describe its kind and location without copying the secret value."
|
|
4146
4338
|
].join(" ");
|
|
4147
|
-
const ignoredWorkspacePaths = new Set([
|
|
4339
|
+
const ignoredWorkspacePaths = /* @__PURE__ */ new Set([
|
|
4148
4340
|
".git",
|
|
4149
4341
|
"node_modules",
|
|
4150
4342
|
"dist",
|
|
@@ -4569,6 +4761,10 @@ function findingFingerprint(finding) {
|
|
|
4569
4761
|
return new Bun.CryptoHasher("sha256").update(basis).digest("hex").slice(0, 16);
|
|
4570
4762
|
}
|
|
4571
4763
|
//#endregion
|
|
4764
|
+
//#region src/review/inline-finding-limits.ts
|
|
4765
|
+
const maxInlineFindingBodyCharacters = 700;
|
|
4766
|
+
const maxInlineFindingBodyLines = 4;
|
|
4767
|
+
//#endregion
|
|
4572
4768
|
//#region src/review/agent/agent-prompt.ts
|
|
4573
4769
|
async function renderAgentPrompt(options) {
|
|
4574
4770
|
const prompt = await renderAgentDefinitionPrompt(options.agent, options.input, options.agentRunContext.prompt);
|
|
@@ -4603,20 +4799,18 @@ function toolsPrompt(diffManifest, toolMode) {
|
|
|
4603
4799
|
].join("\n");
|
|
4604
4800
|
}
|
|
4605
4801
|
function outputPrompt(schema) {
|
|
4606
|
-
|
|
4802
|
+
const lines = compact([
|
|
4607
4803
|
`Schema ID: ${schema.id}.`,
|
|
4608
4804
|
schema.jsonSchema ? `JSON Schema:\n${JSON.stringify(schema.jsonSchema, null, 2)}` : void 0,
|
|
4609
|
-
schema.id === reviewResultSchemaId ? `Example:\n${JSON.stringify(reviewSchemaExample$1(), null, 2)}` : void 0,
|
|
4610
|
-
schema.id === reviewResultSchemaId ? "`suggestedFix` is exact replacement code for the selected range. Do not include Markdown fences, prose, or labels in `suggestedFix`." : void 0,
|
|
4611
|
-
schema.id === reviewResultSchemaId ? "GitHub applies `suggestedFix` to the selected `startLine` through `endLine`. Select the smallest contiguous line span that the replacement code should replace." : void 0,
|
|
4612
|
-
schema.id === reviewResultSchemaId ? "If a fix changes only part of a line, select that whole line and put the full replacement line in `suggestedFix`. If a fix changes multiple lines, select exactly those original lines and put the full replacement block in `suggestedFix`." : void 0,
|
|
4613
|
-
schema.id === reviewResultSchemaId ? "Do not select a larger enclosing block to replace a smaller statement, and do not select one line when the replacement is for a multi-line section. Omit `suggestedFix` if the exact replacement range is uncertain." : void 0,
|
|
4614
|
-
schema.id === reviewResultSchemaId ? "Omit `suggestedFix` for broad rewrites, generated docs/pages, uncertain ranges, or changes better described in prose." : void 0,
|
|
4615
4805
|
"Return exactly one JSON value matching the schema.",
|
|
4616
4806
|
"The first non-whitespace character must be { or [ and the last non-whitespace character must be } or ].",
|
|
4617
|
-
"Do not include Markdown, code fences, prose, explanations, or leading/trailing text."
|
|
4618
|
-
|
|
4619
|
-
|
|
4807
|
+
"Do not include Markdown, code fences, prose, explanations, or leading/trailing text."
|
|
4808
|
+
]);
|
|
4809
|
+
if (schema.id === reviewResultSchemaId) {
|
|
4810
|
+
lines.splice(2, 0, `Example:\n${JSON.stringify(reviewSchemaExample$1(), null, 2)}`, "`suggestedFix` is exact replacement code for the selected range. Do not include Markdown fences, prose, or labels in `suggestedFix`.", "GitHub applies `suggestedFix` to the selected `startLine` through `endLine`. Select the smallest contiguous line span that the replacement code should replace.", "If a fix changes only part of a line, select that whole line and put the full replacement line in `suggestedFix`. If a fix changes multiple lines, select exactly those original lines and put the full replacement block in `suggestedFix`.", "Do not select a larger enclosing block to replace a smaller statement, and do not select one line when the replacement is for a multi-line section. Omit `suggestedFix` if the exact replacement range is uncertain.", "Omit `suggestedFix` for broad rewrites, generated docs/pages, uncertain ranges, or changes better described in prose.");
|
|
4811
|
+
lines.push("For inlineFindings, use only fields shown in the schema and only exact Diff Manifest commentable ranges. If no exact range applies, omit the finding.", `For inlineFindings.body, write the exact inline comment body. Use one short paragraph, at most two sentences, and at most 700 characters. Treat 700 as a hard ceiling, not a target; prefer 250-450 characters when possible.`);
|
|
4812
|
+
}
|
|
4813
|
+
return lines.join("\n\n");
|
|
4620
4814
|
}
|
|
4621
4815
|
function reviewPolicyPrompt(schema) {
|
|
4622
4816
|
if (schema.id !== reviewResultSchemaId) return;
|
|
@@ -4624,7 +4818,9 @@ function reviewPolicyPrompt(schema) {
|
|
|
4624
4818
|
"Review only changed behavior.",
|
|
4625
4819
|
"Report only actionable defects, security risks, regressions, or meaningful test gaps.",
|
|
4626
4820
|
"Put each actionable issue in inlineFindings. Do not leave actionable defects or test gaps only in the summary.",
|
|
4627
|
-
"
|
|
4821
|
+
"Inline finding bodies are final code-review comments, not analysis notes.",
|
|
4822
|
+
`State the concrete defect and user-visible or runtime impact directly. Keep each body to one short paragraph, at most two sentences, and at most 700 characters.`,
|
|
4823
|
+
"Do not include step-by-step reasoning, broad context, praise, restated diff, alternatives, or code snippets unless they are necessary to identify the defect.",
|
|
4628
4824
|
"Omit speculative, style-only, broad refactor, external-fact, and out-of-diff findings.",
|
|
4629
4825
|
"Use read tools when more context is needed. If evidence is insufficient, omit the finding.",
|
|
4630
4826
|
"Emit one inline finding per issue, anchored to the exact Diff Manifest commentable range.",
|
|
@@ -5039,67 +5235,17 @@ function buildRepairPrompt(options) {
|
|
|
5039
5235
|
].join("\n\n");
|
|
5040
5236
|
}
|
|
5041
5237
|
//#endregion
|
|
5042
|
-
//#region package.json
|
|
5043
|
-
var version = "0.3.3";
|
|
5044
|
-
//#endregion
|
|
5045
5238
|
//#region src/review/comment-branding.ts
|
|
5046
5239
|
const piprLogoUrl = "https://pipr.run/images/pipr/pipr-mark.svg";
|
|
5047
5240
|
const piprRepositoryUrl = "https://github.com/somus/pipr";
|
|
5048
5241
|
const mainCommentTitle = `# <img src="${piprLogoUrl}" width="22" height="22" alt=""> Pipr Review`;
|
|
5049
|
-
const mainCommentTitles = new Set([
|
|
5242
|
+
const mainCommentTitles = /* @__PURE__ */ new Set([
|
|
5050
5243
|
"# pipr Review",
|
|
5051
5244
|
"# Pipr Review",
|
|
5052
5245
|
mainCommentTitle
|
|
5053
5246
|
]);
|
|
5054
5247
|
const escapedRepositoryUrl = piprRepositoryUrl.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
5055
|
-
const mainCommentAttributionPattern = new RegExp(`^<sub>Review generated by \\[Pipr\\]\\(${escapedRepositoryUrl}\\) for commit \`[^\`]
|
|
5056
|
-
//#endregion
|
|
5057
|
-
//#region src/review/inline-publication-policy.ts
|
|
5058
|
-
const maxSuggestedFixSelectedLines = 12;
|
|
5059
|
-
const maxSuggestedFixReplacementLines = 20;
|
|
5060
|
-
function inlinePublicationDecision(options) {
|
|
5061
|
-
if (options.existing.markers.has(options.marker) || hasExistingInlinePublicationLocation(options.existing.locations, options.location)) return "skip";
|
|
5062
|
-
return "post";
|
|
5063
|
-
}
|
|
5064
|
-
function isPublishableSuggestedFixSelection(selection) {
|
|
5065
|
-
const suggestedLines = normalizedSuggestedFixLines(selection.suggestedFix);
|
|
5066
|
-
const selectedLineCount = selection.endLine - selection.startLine + 1;
|
|
5067
|
-
if (selection.side !== "RIGHT" || selection.kind === "deleted" || selectedLineCount > maxSuggestedFixSelectedLines || suggestedLines.length > maxSuggestedFixReplacementLines) return false;
|
|
5068
|
-
const originalLines = selectedPreviewLines(selection, selectedLineCount);
|
|
5069
|
-
return Boolean(originalLines && !hasUnchangedSelectionEdge(originalLines, suggestedLines) && !suggestionIncludesUnselectedContext(selection, selectedLineCount, suggestedLines));
|
|
5070
|
-
}
|
|
5071
|
-
function hasExistingInlinePublicationLocation(existing, location) {
|
|
5072
|
-
return existing.some((comment) => {
|
|
5073
|
-
if (comment.path !== location.path || comment.commitId !== location.commitId || comment.side !== location.side) return false;
|
|
5074
|
-
return comment.startLine <= location.endLine && location.startLine <= comment.endLine;
|
|
5075
|
-
});
|
|
5076
|
-
}
|
|
5077
|
-
function normalizedSuggestedFixLines(value) {
|
|
5078
|
-
const normalized = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
5079
|
-
const withoutFinalNewline = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
|
|
5080
|
-
return withoutFinalNewline.length === 0 ? [] : withoutFinalNewline.split("\n");
|
|
5081
|
-
}
|
|
5082
|
-
function selectedPreviewLines(selection, selectedLineCount) {
|
|
5083
|
-
if (!selection.preview) return;
|
|
5084
|
-
const offset = selection.startLine - selection.rangeStartLine;
|
|
5085
|
-
if (offset < 0) return;
|
|
5086
|
-
const previewLines = selection.preview.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
5087
|
-
if (offset + selectedLineCount > previewLines.length) return;
|
|
5088
|
-
return previewLines.slice(offset, offset + selectedLineCount);
|
|
5089
|
-
}
|
|
5090
|
-
function suggestionIncludesUnselectedContext(selection, selectedLineCount, suggestedLines) {
|
|
5091
|
-
if (!selection.preview || suggestedLines.length <= selectedLineCount) return false;
|
|
5092
|
-
const offset = selection.startLine - selection.rangeStartLine;
|
|
5093
|
-
if (offset < 0) return false;
|
|
5094
|
-
const previewLines = selection.preview.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
5095
|
-
return [offset > 0 ? previewLines[offset - 1] : void 0, previewLines[offset + selectedLineCount]].filter((line) => Boolean(line?.trim())).some((line) => suggestedLines.includes(line));
|
|
5096
|
-
}
|
|
5097
|
-
function hasUnchangedSelectionEdge(originalLines, suggestedLines) {
|
|
5098
|
-
const firstLineUnchanged = originalLines[0] === suggestedLines[0];
|
|
5099
|
-
const lastLineUnchanged = originalLines.at(-1) === suggestedLines.at(-1);
|
|
5100
|
-
if (originalLines.length === suggestedLines.length || originalLines.length === 1) return firstLineUnchanged || lastLineUnchanged;
|
|
5101
|
-
return firstLineUnchanged && lastLineUnchanged;
|
|
5102
|
-
}
|
|
5248
|
+
const mainCommentAttributionPattern = new RegExp(`^<sub>Review generated by \\[Pipr\\]\\(${escapedRepositoryUrl}\\) for commit \`[^\`]+\`\\.(?: Config SDK \\d+\\.\\d+\\.\\d+ is behind (?:Pipr \\d+\\.\\d+\\.\\d+|\\[Pipr \\d+\\.\\d+\\.\\d+\\]\\(${escapedRepositoryUrl}/releases/tag/v\\d+\\.\\d+\\.\\d+\\))\\.)?</sub>$`);
|
|
5103
5249
|
//#endregion
|
|
5104
5250
|
//#region src/review/prior-state.ts
|
|
5105
5251
|
const mainCommentMarker = "pipr:main-comment";
|
|
@@ -5358,8 +5504,50 @@ function hashParts(parts) {
|
|
|
5358
5504
|
return new Bun.CryptoHasher("sha256").update(parts.join("\n")).digest("hex").slice(0, 16);
|
|
5359
5505
|
}
|
|
5360
5506
|
//#endregion
|
|
5507
|
+
//#region src/review/suggested-fix-publication-policy.ts
|
|
5508
|
+
const maxSuggestedFixSelectedLines = 12;
|
|
5509
|
+
const maxSuggestedFixReplacementLines = 20;
|
|
5510
|
+
function isPublishableSuggestedFixSelection(selection) {
|
|
5511
|
+
const suggestedLines = normalizedSuggestedFixLines(selection.suggestedFix);
|
|
5512
|
+
const selectedLineCount = selection.endLine - selection.startLine + 1;
|
|
5513
|
+
if (selection.side !== "RIGHT" || selection.kind === "deleted" || selectedLineCount > maxSuggestedFixSelectedLines || suggestedLines.length > maxSuggestedFixReplacementLines) return false;
|
|
5514
|
+
const originalLines = selectedPreviewLines(selection, selectedLineCount);
|
|
5515
|
+
if (!originalLines) return false;
|
|
5516
|
+
const structuralEdgePattern = /^([{}[\]()<>]+)[;,]?$/;
|
|
5517
|
+
const firstOriginalEdge = originalLines[0]?.trim().match(structuralEdgePattern)?.[1];
|
|
5518
|
+
const firstSuggestedEdge = suggestedLines[0]?.trim().match(structuralEdgePattern)?.[1];
|
|
5519
|
+
const lastOriginalEdge = originalLines.at(-1)?.trim().match(structuralEdgePattern)?.[1];
|
|
5520
|
+
const lastSuggestedEdge = suggestedLines.at(-1)?.trim().match(structuralEdgePattern)?.[1];
|
|
5521
|
+
return (firstOriginalEdge === void 0 || firstOriginalEdge === firstSuggestedEdge) && (lastOriginalEdge === void 0 || lastOriginalEdge === lastSuggestedEdge) && !hasUnchangedSelectionEdge(originalLines, suggestedLines) && !suggestionIncludesUnselectedContext(selection, selectedLineCount, suggestedLines);
|
|
5522
|
+
}
|
|
5523
|
+
function normalizedSuggestedFixLines(value) {
|
|
5524
|
+
const normalized = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
5525
|
+
const withoutFinalNewline = normalized.endsWith("\n") ? normalized.slice(0, -1) : normalized;
|
|
5526
|
+
return withoutFinalNewline.length === 0 ? [] : withoutFinalNewline.split("\n");
|
|
5527
|
+
}
|
|
5528
|
+
function selectedPreviewLines(selection, selectedLineCount) {
|
|
5529
|
+
if (!selection.preview) return;
|
|
5530
|
+
const offset = selection.startLine - selection.rangeStartLine;
|
|
5531
|
+
if (offset < 0) return;
|
|
5532
|
+
const previewLines = selection.preview.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
5533
|
+
if (offset + selectedLineCount > previewLines.length) return;
|
|
5534
|
+
return previewLines.slice(offset, offset + selectedLineCount);
|
|
5535
|
+
}
|
|
5536
|
+
function suggestionIncludesUnselectedContext(selection, selectedLineCount, suggestedLines) {
|
|
5537
|
+
if (!selection.preview || suggestedLines.length <= selectedLineCount) return false;
|
|
5538
|
+
const offset = selection.startLine - selection.rangeStartLine;
|
|
5539
|
+
if (offset < 0) return false;
|
|
5540
|
+
const previewLines = selection.preview.replace(/\r\n/g, "\n").replace(/\r/g, "\n").split("\n");
|
|
5541
|
+
return [offset > 0 ? previewLines[offset - 1] : void 0, previewLines[offset + selectedLineCount]].filter((line) => Boolean(line?.trim())).some((line) => suggestedLines.includes(line));
|
|
5542
|
+
}
|
|
5543
|
+
function hasUnchangedSelectionEdge(originalLines, suggestedLines) {
|
|
5544
|
+
const firstLineUnchanged = originalLines[0] === suggestedLines[0];
|
|
5545
|
+
const lastLineUnchanged = originalLines.at(-1) === suggestedLines.at(-1);
|
|
5546
|
+
if (originalLines.length === suggestedLines.length || originalLines.length === 1) return firstLineUnchanged || lastLineUnchanged;
|
|
5547
|
+
return firstLineUnchanged && lastLineUnchanged;
|
|
5548
|
+
}
|
|
5549
|
+
//#endregion
|
|
5361
5550
|
//#region src/review/comment.ts
|
|
5362
|
-
const runtimeVersion = version;
|
|
5363
5551
|
const inlinePublicationItemSchema = z.strictObject({
|
|
5364
5552
|
finding: reviewFindingSchema,
|
|
5365
5553
|
range: commentableRangeSchema,
|
|
@@ -5406,6 +5594,7 @@ const threadActionSchema = z.strictObject({
|
|
|
5406
5594
|
const threadActionsSchema = z.array(threadActionSchema);
|
|
5407
5595
|
const publicationMetadataSchema = z.strictObject({
|
|
5408
5596
|
runtimeVersion: z.string().min(1),
|
|
5597
|
+
configVersion: z.string().min(1).optional(),
|
|
5409
5598
|
trustedConfigSha: z.string().min(1).optional(),
|
|
5410
5599
|
trustedConfigHash: z.string().min(1).optional(),
|
|
5411
5600
|
reviewedHeadSha: z.string().min(1),
|
|
@@ -5425,8 +5614,6 @@ const publicationPlanSchema = z.strictObject({
|
|
|
5425
5614
|
reviewState: priorReviewStateSchema,
|
|
5426
5615
|
threadActions: threadActionsSchema
|
|
5427
5616
|
});
|
|
5428
|
-
const maxInlineFindingBodyCharacters = 700;
|
|
5429
|
-
const maxInlineFindingBodyLines = 4;
|
|
5430
5617
|
function buildPublicationPlan(options) {
|
|
5431
5618
|
const reviewState = options.reviewState ?? buildPriorReviewState({
|
|
5432
5619
|
findings: options.inlineItems.map((item) => item.finding),
|
|
@@ -5443,7 +5630,7 @@ function buildPublicationPlan(options) {
|
|
|
5443
5630
|
event: options.event,
|
|
5444
5631
|
reviewState,
|
|
5445
5632
|
main: options.main,
|
|
5446
|
-
|
|
5633
|
+
metadata
|
|
5447
5634
|
}),
|
|
5448
5635
|
mainMarker: mainCommentMarker,
|
|
5449
5636
|
changeNumber: options.event.change.number,
|
|
@@ -5497,9 +5684,9 @@ function findingWithPublishableBody(finding) {
|
|
|
5497
5684
|
};
|
|
5498
5685
|
}
|
|
5499
5686
|
function conciseInlineFindingBody(value) {
|
|
5500
|
-
const body = redactPotentialSecrets((value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim().split(/\n{2,}/)[0] ?? value).split("\n").slice(0,
|
|
5501
|
-
if (body.length <=
|
|
5502
|
-
return `${body.slice(0,
|
|
5687
|
+
const body = redactPotentialSecrets((value.replace(/\r\n/g, "\n").replace(/\r/g, "\n").trim().split(/\n{2,}/)[0] ?? value).split("\n").slice(0, 4).join("\n").trim());
|
|
5688
|
+
if (body.length <= 700) return body;
|
|
5689
|
+
return `${body.slice(0, 700).trimEnd()}...`;
|
|
5503
5690
|
}
|
|
5504
5691
|
function findingWithPublishableSuggestedFix(finding, range) {
|
|
5505
5692
|
if (!finding.suggestedFix) return finding;
|
|
@@ -5531,10 +5718,19 @@ function renderMainComment(options) {
|
|
|
5531
5718
|
"",
|
|
5532
5719
|
redactPotentialSecrets(options.main),
|
|
5533
5720
|
"",
|
|
5534
|
-
|
|
5721
|
+
renderMainCommentAttribution(options.metadata),
|
|
5535
5722
|
""
|
|
5536
5723
|
].join("\n");
|
|
5537
5724
|
}
|
|
5725
|
+
function renderMainCommentAttribution(metadata) {
|
|
5726
|
+
const configNotice = configVersionNotice(metadata);
|
|
5727
|
+
return `<sub>Review generated by [Pipr](${piprRepositoryUrl}) for commit \`${metadata.reviewedHeadSha.slice(0, 7)}\`.${configNotice}</sub>`;
|
|
5728
|
+
}
|
|
5729
|
+
function configVersionNotice(metadata) {
|
|
5730
|
+
if (!metadata.configVersion || !stableSemverPattern.test(metadata.runtimeVersion) || !stableSemverPattern.test(metadata.configVersion) || compareStableSemver(metadata.runtimeVersion, metadata.configVersion) <= 0) return "";
|
|
5731
|
+
const releaseUrl = `${piprRepositoryUrl}/releases/tag/v${metadata.runtimeVersion}`;
|
|
5732
|
+
return ` Config SDK ${metadata.configVersion} is behind [Pipr ${metadata.runtimeVersion}](${releaseUrl}).`;
|
|
5733
|
+
}
|
|
5538
5734
|
function renderInlineBody(finding, findingId, reviewedHeadSha) {
|
|
5539
5735
|
const findingBody = startsWithStructuredMarkdown(finding.body) ? finding.body : [
|
|
5540
5736
|
"**Issue**",
|
|
@@ -5966,9 +6162,9 @@ function trackResultFindingScope(state, value, paths) {
|
|
|
5966
6162
|
const parsed = agentInlineFindingsOutputSchema.safeParse(value);
|
|
5967
6163
|
if (parsed.success) state.findingScopes.set(parsed.data.inlineFindings, paths);
|
|
5968
6164
|
}
|
|
5969
|
-
function collectedReview(output) {
|
|
6165
|
+
function collectedReview(output, summaryBody) {
|
|
5970
6166
|
return {
|
|
5971
|
-
summary: { body:
|
|
6167
|
+
summary: { body: summaryBody },
|
|
5972
6168
|
inlineFindings: output.findings.map((item) => item.finding)
|
|
5973
6169
|
};
|
|
5974
6170
|
}
|
|
@@ -6009,7 +6205,8 @@ async function runTaskRuntime(options) {
|
|
|
6009
6205
|
reason: options.emptyTasksReason,
|
|
6010
6206
|
taskName: options.taskName,
|
|
6011
6207
|
trustedConfigSha: options.trustedConfigSha,
|
|
6012
|
-
trustedConfigHash: options.trustedConfigHash
|
|
6208
|
+
trustedConfigHash: options.trustedConfigHash,
|
|
6209
|
+
versionCompatibility: options.versionCompatibility
|
|
6013
6210
|
});
|
|
6014
6211
|
}
|
|
6015
6212
|
const selectedTasks = tasks.map((task) => task.name);
|
|
@@ -6104,7 +6301,8 @@ async function runTaskRuntime(options) {
|
|
|
6104
6301
|
});
|
|
6105
6302
|
if (commandResponse) return commandResponse;
|
|
6106
6303
|
assertReviewCommentOutput(output, options.commandInvocation !== void 0);
|
|
6107
|
-
const
|
|
6304
|
+
const main = typeof output.comment.value === "string" ? output.comment.value : output.comment.value.main ?? "Review completed.";
|
|
6305
|
+
const validated = validateReviewResult(collectedReview(output, main), diffManifest, {
|
|
6108
6306
|
expectedHeadSha: options.event.change.head.sha,
|
|
6109
6307
|
pathScopeForFinding: (_finding, index) => output.findings[index]?.paths
|
|
6110
6308
|
});
|
|
@@ -6118,7 +6316,7 @@ async function runTaskRuntime(options) {
|
|
|
6118
6316
|
});
|
|
6119
6317
|
const publishing = buildCommentPublishingPlan({
|
|
6120
6318
|
event: options.event,
|
|
6121
|
-
main
|
|
6319
|
+
main,
|
|
6122
6320
|
validated,
|
|
6123
6321
|
manifest: diffManifest,
|
|
6124
6322
|
maxInlineComments: config.publication.maxInlineComments,
|
|
@@ -6126,6 +6324,7 @@ async function runTaskRuntime(options) {
|
|
|
6126
6324
|
threadActions: verifier.threadActions,
|
|
6127
6325
|
metadata: {
|
|
6128
6326
|
runtimeVersion,
|
|
6327
|
+
configVersion: options.versionCompatibility?.configVersion,
|
|
6129
6328
|
trustedConfigSha: options.trustedConfigSha,
|
|
6130
6329
|
trustedConfigHash: options.trustedConfigHash,
|
|
6131
6330
|
reviewedHeadSha: options.event.change.head.sha,
|
|
@@ -6316,6 +6515,7 @@ function skippedTaskRuntimeResult(options) {
|
|
|
6316
6515
|
maxInlineComments: options.config.publication.maxInlineComments,
|
|
6317
6516
|
metadata: {
|
|
6318
6517
|
runtimeVersion,
|
|
6518
|
+
configVersion: options.versionCompatibility?.configVersion,
|
|
6319
6519
|
trustedConfigSha: options.trustedConfigSha,
|
|
6320
6520
|
trustedConfigHash: options.trustedConfigHash,
|
|
6321
6521
|
reviewedHeadSha: options.event.change.head.sha,
|
|
@@ -6384,7 +6584,7 @@ const githubRepositoryPermissionResponseSchema = z.looseObject({
|
|
|
6384
6584
|
permission: z.string().min(1),
|
|
6385
6585
|
role_name: z.string().min(1).optional()
|
|
6386
6586
|
});
|
|
6387
|
-
const permissionLevels = new Set([
|
|
6587
|
+
const permissionLevels = /* @__PURE__ */ new Set([
|
|
6388
6588
|
"read",
|
|
6389
6589
|
"triage",
|
|
6390
6590
|
"write",
|
|
@@ -7043,6 +7243,18 @@ async function loadGitHubPriorMainComment(options) {
|
|
|
7043
7243
|
}), "pipr:main-comment", options.change.change.number, ownerLogin)?.body ?? void 0;
|
|
7044
7244
|
}
|
|
7045
7245
|
//#endregion
|
|
7246
|
+
//#region src/review/inline-publication-policy.ts
|
|
7247
|
+
function inlinePublicationDecision(options) {
|
|
7248
|
+
if (options.existing.markers.has(options.marker) || hasExistingInlinePublicationLocation(options.existing.locations, options.location)) return "skip";
|
|
7249
|
+
return "post";
|
|
7250
|
+
}
|
|
7251
|
+
function hasExistingInlinePublicationLocation(existing, location) {
|
|
7252
|
+
return existing.some((comment) => {
|
|
7253
|
+
if (comment.path !== location.path || comment.commitId !== location.commitId || comment.side !== location.side) return false;
|
|
7254
|
+
return comment.startLine <= location.endLine && location.startLine <= comment.endLine;
|
|
7255
|
+
});
|
|
7256
|
+
}
|
|
7257
|
+
//#endregion
|
|
7046
7258
|
//#region src/hosts/github/inline.ts
|
|
7047
7259
|
const githubReviewCommentLocationSchema = z.strictObject({
|
|
7048
7260
|
path: z.string().min(1),
|
|
@@ -7630,6 +7842,10 @@ function logTrustedRuntime(log, runtime) {
|
|
|
7630
7842
|
tasks: runtime.plan.tasks.length,
|
|
7631
7843
|
commands: runtime.plan.commands.length
|
|
7632
7844
|
});
|
|
7845
|
+
logConfigWarnings(log, runtime.settings.warnings);
|
|
7846
|
+
}
|
|
7847
|
+
function logConfigWarnings(log, warnings) {
|
|
7848
|
+
for (const warning of warnings) log.warning("config warning", { warning });
|
|
7633
7849
|
}
|
|
7634
7850
|
function addProviderSecrets(log, config, env) {
|
|
7635
7851
|
for (const provider of config.providers) log.addSecret((env ?? process.env)[provider.apiKeyEnv]);
|
|
@@ -7825,6 +8041,7 @@ async function runTrustedReviewAndPublish(options) {
|
|
|
7825
8041
|
event: options.event,
|
|
7826
8042
|
env: options.options.env,
|
|
7827
8043
|
plan: options.trustedRuntime.plan,
|
|
8044
|
+
versionCompatibility: options.trustedRuntime.versionCompatibility,
|
|
7828
8045
|
taskName: options.taskName,
|
|
7829
8046
|
taskInput: options.taskInput,
|
|
7830
8047
|
commandInvocation: options.commandInvocation,
|
|
@@ -7896,7 +8113,8 @@ async function runTrustedReviewAndPublish(options) {
|
|
|
7896
8113
|
async function loadRuntimeProjectFromGitCommit(options) {
|
|
7897
8114
|
const configDir = resolveContainedConfigDir(options);
|
|
7898
8115
|
const files = listConfigFilesAtCommit(options.rootDir, options.commitSha, configDir.gitPath);
|
|
7899
|
-
|
|
8116
|
+
const configPath = configDir.gitPath === "." ? "config.ts" : `${configDir.gitPath}/config.ts`;
|
|
8117
|
+
if (files.length === 0 || !files.some((file) => file.path === configPath)) throw new Error(`No Pipr config found at ${configPath} in base commit ${options.commitSha}. Run \`pipr init\` and commit the generated config.`);
|
|
7900
8118
|
const tempRoot = await mkdtemp(path.join(os.tmpdir(), "pipr-base-config-"));
|
|
7901
8119
|
try {
|
|
7902
8120
|
const hash = new Bun.CryptoHasher("sha256");
|
|
@@ -8434,7 +8652,10 @@ async function runInspectCommand(options) {
|
|
|
8434
8652
|
...options,
|
|
8435
8653
|
requireProviderEnv: false
|
|
8436
8654
|
});
|
|
8437
|
-
return
|
|
8655
|
+
return {
|
|
8656
|
+
...inspectRuntimePlan(runtime.plan, runtime.settings.source),
|
|
8657
|
+
warnings: runtime.settings.warnings
|
|
8658
|
+
};
|
|
8438
8659
|
}
|
|
8439
8660
|
/** Loads the runtime config and pull request event without running review publication. */
|
|
8440
8661
|
async function runDryRunCommand(options) {
|
|
@@ -8453,7 +8674,8 @@ async function runDryRunCommand(options) {
|
|
|
8453
8674
|
});
|
|
8454
8675
|
return {
|
|
8455
8676
|
configSource: runtime.settings.source,
|
|
8456
|
-
event
|
|
8677
|
+
event,
|
|
8678
|
+
warnings: runtime.settings.warnings
|
|
8457
8679
|
};
|
|
8458
8680
|
}
|
|
8459
8681
|
/** Runs configured change-request tasks against local Git base and head revisions. */
|
|
@@ -8478,6 +8700,7 @@ async function runLocalReviewCommand(options) {
|
|
|
8478
8700
|
tasks: runtime.plan.tasks.length,
|
|
8479
8701
|
commands: runtime.plan.commands.length
|
|
8480
8702
|
});
|
|
8703
|
+
if (log) logConfigWarnings(log, runtime.settings.warnings);
|
|
8481
8704
|
const selectedTasks = selectLocalReviewTasks(runtime.plan);
|
|
8482
8705
|
const includeWorkingTree = options.headSha === void 0;
|
|
8483
8706
|
const headSha = options.headSha ?? runGit$1(["rev-parse", "HEAD"], options.rootDir).trim();
|
|
@@ -8500,6 +8723,7 @@ async function runLocalReviewCommand(options) {
|
|
|
8500
8723
|
event,
|
|
8501
8724
|
env: options.env,
|
|
8502
8725
|
plan: runtime.plan,
|
|
8726
|
+
versionCompatibility: runtime.versionCompatibility,
|
|
8503
8727
|
selectedTasks,
|
|
8504
8728
|
emptyTasksReason: "No change-request tasks are configured for local review",
|
|
8505
8729
|
piExecutable: options.piExecutable,
|
|
@@ -8550,6 +8774,6 @@ async function runActionCommandWithDependencies(options) {
|
|
|
8550
8774
|
});
|
|
8551
8775
|
}
|
|
8552
8776
|
//#endregion
|
|
8553
|
-
export { runInspectCommand as a, PublicationError as c,
|
|
8777
|
+
export { piRequiredCliFlags as _, runInspectCommand as a, PublicationError as c, maxInlineFindingBodyLines as d, supportedOfficialInitAdapters as f, piReadOnlyToolNames as g, piBuiltinToolNames as h, runInitCommand as i, isPublishableSuggestedFixSelection as l, supportedOfficialInitRecipes as m, runActionCommandWithDependencies as n, runLocalReviewCommand as o, listOfficialInitRecipes as p, runDryRunCommand as r, runValidateCommand as s, runActionCommand as t, maxInlineFindingBodyCharacters as u, piThinkingLevels as v };
|
|
8554
8778
|
|
|
8555
|
-
//# sourceMappingURL=commands-
|
|
8779
|
+
//# sourceMappingURL=commands-DCpIxJkz.mjs.map
|