@riddledc/riddle-proof 0.5.5 → 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,11 +2,13 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import { execSync } from "node:child_process";
3
3
  import {
4
4
  existsSync,
5
+ copyFileSync,
5
6
  lstatSync,
6
7
  mkdirSync,
7
8
  readFileSync,
9
+ renameSync,
10
+ realpathSync,
8
11
  rmSync,
9
- symlinkSync,
10
12
  unlinkSync,
11
13
  writeFileSync,
12
14
  } from "node:fs";
@@ -276,6 +278,7 @@ export function ensureWorktree({
276
278
  }
277
279
 
278
280
  const DEPS_MANIFEST = ".workspace-core-deps.json";
281
+ const DEPS_INPUT_FILES = ["package.json", "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock"];
279
282
 
280
283
  function depsManifestPath(projectDir) {
281
284
  return path.join(projectDir, "node_modules", DEPS_MANIFEST);
@@ -285,7 +288,7 @@ export function computeDependencyFingerprint(projectDir) {
285
288
  const packageJson = path.join(projectDir, "package.json");
286
289
  if (!existsSync(packageJson)) return "";
287
290
  const digest = createHash("sha256");
288
- for (const name of ["package.json", "package-lock.json", "npm-shrinkwrap.json", "pnpm-lock.yaml", "yarn.lock"]) {
291
+ for (const name of DEPS_INPUT_FILES) {
289
292
  const filePath = path.join(projectDir, name);
290
293
  if (!existsSync(filePath)) continue;
291
294
  digest.update(name);
@@ -318,6 +321,110 @@ function writeDepsManifest(projectDir, fingerprint, installCmd) {
318
321
  writeFileSync(manifestPath, JSON.stringify({ fingerprint, install_cmd: installCmd }, null, 2));
319
322
  }
320
323
 
324
+ function dependencyCacheRoot(projectDir) {
325
+ if (process.env.RIDDLE_PROOF_DISABLE_DEPS_CACHE === "1") return "";
326
+ const configured = (process.env.RIDDLE_PROOF_DEPS_CACHE_ROOT || "").trim();
327
+ if (configured) return path.resolve(configured);
328
+
329
+ const resolved = path.resolve(projectDir);
330
+ const worktreeMarker = `${path.sep}.riddle-proof-worktrees${path.sep}`;
331
+ const worktreeIndex = resolved.indexOf(worktreeMarker);
332
+ if (worktreeIndex >= 0) {
333
+ return path.join(resolved.slice(0, worktreeIndex), ".riddle-proof-deps-cache");
334
+ }
335
+ return path.join(path.dirname(resolved), ".riddle-proof-deps-cache");
336
+ }
337
+
338
+ function dependencyCacheKey(fingerprint, installCmd) {
339
+ const nodeMajor = (process.versions.node || "").split(".")[0] || "unknown";
340
+ return [
341
+ sanitizeFragment(installCmd, "install"),
342
+ process.platform,
343
+ process.arch,
344
+ `node${sanitizeFragment(nodeMajor, "unknown")}`,
345
+ fingerprint.slice(0, 24),
346
+ ].join("-");
347
+ }
348
+
349
+ function copyDependencyInputs(sourceDir, targetDir) {
350
+ mkdirSync(targetDir, { recursive: true });
351
+ for (const name of DEPS_INPUT_FILES) {
352
+ const sourcePath = path.join(sourceDir, name);
353
+ if (existsSync(sourcePath)) {
354
+ copyFileSync(sourcePath, path.join(targetDir, name));
355
+ }
356
+ }
357
+ }
358
+
359
+ function materializeNodeModules(projectDir, sourceModules) {
360
+ const projectModules = path.join(projectDir, "node_modules");
361
+ removePath(projectModules);
362
+ const resolvedSource = realpathSync(sourceModules);
363
+ const hardlinkResult = runSafe(
364
+ `cp -al ${shellQuote(resolvedSource)} ${shellQuote(projectModules)}`,
365
+ undefined,
366
+ dependencyInstallTimeoutMs(),
367
+ );
368
+ if (hardlinkResult.ok && existsSync(projectModules)) return "hardlinked";
369
+
370
+ removePath(projectModules);
371
+ const copyResult = runSafe(
372
+ `cp -a ${shellQuote(resolvedSource)} ${shellQuote(projectModules)}`,
373
+ undefined,
374
+ dependencyInstallTimeoutMs(),
375
+ );
376
+ if (!copyResult.ok || !existsSync(projectModules)) {
377
+ throw new Error(`dependency materialization failed in ${projectDir}: ${copyResult.output.slice(0, 300)}`);
378
+ }
379
+ return "copied";
380
+ }
381
+
382
+ function tryEnsureCachedDeps({ projectDir, fingerprint, installCmd }) {
383
+ const cacheRoot = dependencyCacheRoot(projectDir);
384
+ if (!cacheRoot) return "";
385
+
386
+ const cacheDir = path.join(cacheRoot, dependencyCacheKey(fingerprint, installCmd));
387
+ const cacheModules = path.join(cacheDir, "node_modules");
388
+ const cacheManifest = readDepsManifest(cacheDir);
389
+ if (cacheManifest.fingerprint === fingerprint && cacheManifest.install_cmd === installCmd && existsSync(cacheModules)) {
390
+ materializeNodeModules(projectDir, cacheModules);
391
+ return `reused_cache:${cacheDir}`;
392
+ }
393
+
394
+ const tempCacheDir = `${cacheDir}.tmp-${process.pid}-${randomUUID().slice(0, 8)}`;
395
+ try {
396
+ mkdirSync(path.dirname(cacheDir), { recursive: true });
397
+ removePath(tempCacheDir);
398
+ copyDependencyInputs(projectDir, tempCacheDir);
399
+
400
+ const installResult = runSafe(`${installCmd} 2>&1 | tail -5`, tempCacheDir, dependencyInstallTimeoutMs());
401
+ if (!installResult.ok) {
402
+ removePath(tempCacheDir);
403
+ return "";
404
+ }
405
+ writeDepsManifest(tempCacheDir, fingerprint, installCmd);
406
+
407
+ if (!existsSync(cacheDir)) {
408
+ try {
409
+ renameSync(tempCacheDir, cacheDir);
410
+ } catch {
411
+ removePath(tempCacheDir);
412
+ }
413
+ } else {
414
+ removePath(tempCacheDir);
415
+ }
416
+
417
+ const finalManifest = readDepsManifest(cacheDir);
418
+ if (finalManifest.fingerprint === fingerprint && finalManifest.install_cmd === installCmd && existsSync(cacheModules)) {
419
+ materializeNodeModules(projectDir, cacheModules);
420
+ return `cached:${installCmd}`;
421
+ }
422
+ } catch {
423
+ removePath(tempCacheDir);
424
+ }
425
+ return "";
426
+ }
427
+
321
428
  function dependencyInstallTimeoutMs() {
322
429
  const parsed = Number.parseInt(process.env.RIDDLE_PROOF_INSTALL_TIMEOUT_MS || "", 10);
323
430
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 600000;
@@ -337,15 +444,17 @@ export function ensureDeps({ projectDir, reuseFrom = "" } = {}) {
337
444
  const sourceManifest = readDepsManifest(reuseFrom);
338
445
  const sourceModules = path.join(reuseFrom, "node_modules");
339
446
  if (sourceFingerprint === fingerprint && sourceManifest.fingerprint === fingerprint && existsSync(sourceModules)) {
340
- const projectModules = path.join(projectDir, "node_modules");
341
- removePath(projectModules);
342
- symlinkSync(sourceModules, projectModules);
447
+ materializeNodeModules(projectDir, sourceModules);
343
448
  return `reused_from:${reuseFrom}`;
344
449
  }
345
450
  }
346
451
 
347
452
  const installCmd = detectInstallCommand(projectDir);
348
453
  if (!installCmd) return "no_install_command";
454
+ const cachedStatus = tryEnsureCachedDeps({ projectDir, fingerprint, installCmd });
455
+ if (cachedStatus) return cachedStatus;
456
+
457
+ removePath(path.join(projectDir, "node_modules"));
349
458
  const installResult = runSafe(`${installCmd} 2>&1 | tail -5`, projectDir, dependencyInstallTimeoutMs());
350
459
  if (!installResult.ok) {
351
460
  throw new Error(`dependency install failed in ${projectDir}: ${installResult.output.slice(0, 300)}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@riddledc/riddle-proof",
3
- "version": "0.5.5",
3
+ "version": "0.5.7",
4
4
  "description": "Reusable Riddle Proof contracts and helpers for evidence-backed agent changes.",
5
5
  "license": "MIT",
6
6
  "author": "RiddleDC",