|
1 | 1 | #!/usr/bin/env node |
2 | 2 |
|
3 | 3 | import { spawnSync } from "node:child_process"; |
4 | | -import { existsSync, readFileSync } from "node:fs"; |
| 4 | +import { existsSync, readFileSync, statSync } from "node:fs"; |
5 | 5 | import { access } from "node:fs/promises"; |
6 | 6 | import module from "node:module"; |
| 7 | +import os from "node:os"; |
| 8 | +import path from "node:path"; |
7 | 9 | import { fileURLToPath } from "node:url"; |
8 | 10 |
|
9 | 11 | const MIN_NODE_MAJOR = 22; |
@@ -46,6 +48,41 @@ const isSourceCheckoutLauncher = () => |
46 | 48 | const isNodeCompileCacheDisabled = () => process.env.NODE_DISABLE_COMPILE_CACHE !== undefined; |
47 | 49 | const isNodeCompileCacheRequested = () => |
48 | 50 | process.env.NODE_COMPILE_CACHE !== undefined && !isNodeCompileCacheDisabled(); |
| 51 | +const sanitizeCompileCachePathSegment = (value) => { |
| 52 | + const normalized = value.replace(/[^A-Za-z0-9._-]+/g, "_").replace(/^_+|_+$/g, ""); |
| 53 | + return normalized.length > 0 ? normalized : "unknown"; |
| 54 | +}; |
| 55 | +const readPackageVersion = () => { |
| 56 | + try { |
| 57 | + const parsed = JSON.parse(readFileSync(new URL("./package.json", import.meta.url), "utf8")); |
| 58 | + if (typeof parsed?.version === "string" && parsed.version.trim().length > 0) { |
| 59 | + return parsed.version; |
| 60 | + } |
| 61 | + } catch { |
| 62 | + // Fall through to an install-metadata-only cache key. |
| 63 | + } |
| 64 | + return "unknown"; |
| 65 | +}; |
| 66 | +const resolvePackagedCompileCacheDirectory = () => { |
| 67 | + const packageJsonUrl = new URL("./package.json", import.meta.url); |
| 68 | + const version = sanitizeCompileCachePathSegment(readPackageVersion()); |
| 69 | + let installMarker = "no-package-json"; |
| 70 | + try { |
| 71 | + const stat = statSync(packageJsonUrl); |
| 72 | + installMarker = `${Math.trunc(stat.mtimeMs)}-${stat.size}`; |
| 73 | + } catch { |
| 74 | + // Package archives should always have package.json, but keep startup best-effort. |
| 75 | + } |
| 76 | + const baseDirectory = isNodeCompileCacheRequested() |
| 77 | + ? process.env.NODE_COMPILE_CACHE |
| 78 | + : path.join(os.tmpdir(), "node-compile-cache"); |
| 79 | + return path.join( |
| 80 | + baseDirectory, |
| 81 | + "openclaw", |
| 82 | + version, |
| 83 | + sanitizeCompileCachePathSegment(installMarker), |
| 84 | + ); |
| 85 | +}; |
49 | 86 |
|
50 | 87 | const respawnWithoutCompileCacheIfNeeded = () => { |
51 | 88 | if (!isSourceCheckoutLauncher()) { |
@@ -79,10 +116,46 @@ const respawnWithoutCompileCacheIfNeeded = () => { |
79 | 116 |
|
80 | 117 | respawnWithoutCompileCacheIfNeeded(); |
81 | 118 |
|
| 119 | +const respawnWithPackagedCompileCacheIfNeeded = () => { |
| 120 | + if (isSourceCheckoutLauncher() || isNodeCompileCacheDisabled()) { |
| 121 | + return false; |
| 122 | + } |
| 123 | + if (process.env.OPENCLAW_PACKAGED_COMPILE_CACHE_RESPAWNED === "1") { |
| 124 | + return false; |
| 125 | + } |
| 126 | + const currentDirectory = module.getCompileCacheDir?.(); |
| 127 | + if (!currentDirectory) { |
| 128 | + return false; |
| 129 | + } |
| 130 | + const desiredDirectory = resolvePackagedCompileCacheDirectory(); |
| 131 | + if (path.resolve(currentDirectory) === path.resolve(desiredDirectory)) { |
| 132 | + return false; |
| 133 | + } |
| 134 | + const env = { |
| 135 | + ...process.env, |
| 136 | + NODE_COMPILE_CACHE: desiredDirectory, |
| 137 | + OPENCLAW_PACKAGED_COMPILE_CACHE_RESPAWNED: "1", |
| 138 | + }; |
| 139 | + const result = spawnSync( |
| 140 | + process.execPath, |
| 141 | + [...process.execArgv, fileURLToPath(import.meta.url), ...process.argv.slice(2)], |
| 142 | + { |
| 143 | + stdio: "inherit", |
| 144 | + env, |
| 145 | + }, |
| 146 | + ); |
| 147 | + if (result.error) { |
| 148 | + throw result.error; |
| 149 | + } |
| 150 | + process.exit(result.status ?? 1); |
| 151 | +}; |
| 152 | + |
| 153 | +respawnWithPackagedCompileCacheIfNeeded(); |
| 154 | + |
82 | 155 | // https://nodejs.org/api/module.html#module-compile-cache |
83 | 156 | if (module.enableCompileCache && !isNodeCompileCacheDisabled() && !isSourceCheckoutLauncher()) { |
84 | 157 | try { |
85 | | - module.enableCompileCache(); |
| 158 | + module.enableCompileCache(resolvePackagedCompileCacheDirectory()); |
86 | 159 | } catch { |
87 | 160 | // Ignore errors |
88 | 161 | } |
|
0 commit comments