@agimon-ai/doompi 0.0.1-alpha.50 → 0.0.1-alpha.53
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/src/adapters/bootstrapLocator.cjs +1 -1
- package/dist/src/adapters/bootstrapLocator.cjs.map +1 -1
- package/dist/src/adapters/bootstrapLocator.d.cts.map +1 -1
- package/dist/src/adapters/bootstrapLocator.d.mts.map +1 -1
- package/dist/src/adapters/bootstrapLocator.mjs +1 -1
- package/dist/src/adapters/bootstrapLocator.mjs.map +1 -1
- package/dist/src/adapters/layerPackageInstaller.cjs +2 -2
- package/dist/src/adapters/layerPackageInstaller.cjs.map +1 -1
- package/dist/src/adapters/layerPackageInstaller.d.cts.map +1 -1
- package/dist/src/adapters/layerPackageInstaller.d.mts.map +1 -1
- package/dist/src/adapters/layerPackageInstaller.mjs +2 -2
- package/dist/src/adapters/layerPackageInstaller.mjs.map +1 -1
- package/dist/src/adapters/syncDrift.cjs +1 -1
- package/dist/src/adapters/syncDrift.cjs.map +1 -1
- package/dist/src/adapters/syncDrift.d.cts +2 -0
- package/dist/src/adapters/syncDrift.d.cts.map +1 -1
- package/dist/src/adapters/syncDrift.d.mts +2 -0
- package/dist/src/adapters/syncDrift.d.mts.map +1 -1
- package/dist/src/adapters/syncDrift.mjs +1 -1
- package/dist/src/adapters/syncDrift.mjs.map +1 -1
- package/package.json +13 -13
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./repository/repository.cjs"),n=require("./syncRegistration.cjs");require("./syncStateContract.cjs");const r=require("./syncState.cjs");let i=require("node:url"),a=require("node:path");a=e.__toESM(a,1);let o=require("node:crypto");o=e.__toESM(o,1);let s=require("node:fs");s=e.__toESM(s,1);let c=require("node:os");c=e.__toESM(c,1);const l=new Map;function u(e){try{return o.default.createHash(`sha256`).update(s.default.readFileSync(e)).digest(`hex`)}catch{return}}function d(e){let t;try{t=s.default.statSync(e)}catch{return}if(!t.isFile())return;let n=u(e);if(n!==void 0)return{path:e,size:t.size,mtimeMs:t.mtimeMs,sha256:n}}function f(e){if(m(e)&&typeof e.path==`string`&&typeof e.size==`number`&&typeof e.mtimeMs==`number`&&typeof e.sha256==`string`)return{path:e.path,size:e.size,mtimeMs:e.mtimeMs,sha256:e.sha256}}function p(e){let t=[],n=o.default.createHash(`sha256`);for(let r of e){let e;try{e=s.default.statSync(r.path)}catch{return!1}if(!e.isFile())return!1;n.update(`${r.path} ${String(e.size)} ${String(e.mtimeMs)} `),(e.size!==r.size||e.mtimeMs!==r.mtimeMs)&&t.push(r)}if(t.length===0)return!0;let r=n.digest(`hex`),i=l.get(r);if(i!==void 0)return i;let a=t.every(e=>u(e.path)===e.sha256);return l.set(r,a),a}function m(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function h(e){return m(e)?Object.fromEntries(Object.entries(e).filter(e=>typeof e[1]==`string`)):{}}function g(e){let t=a.default.resolve(e);try{return s.default.realpathSync(t)}catch{let e=a.default.dirname(t);return e===t?t:a.default.join(g(e),a.default.basename(t))}}function _(e,t){let n=a.default.relative(g(e),g(t));return n===``||!n.startsWith(`..`)&&!a.default.isAbsolute(n)}function v(e=require("url").pathToFileURL(__filename).href){let t=e.endsWith(`.ts`)?`ts`:`mjs`,
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./repository/repository.cjs"),n=require("./syncRegistration.cjs");require("./syncStateContract.cjs");const r=require("./syncState.cjs");let i=require("node:url"),a=require("node:path");a=e.__toESM(a,1);let o=require("node:crypto");o=e.__toESM(o,1);let s=require("node:fs");s=e.__toESM(s,1);let c=require("node:os");c=e.__toESM(c,1);const l=new Map;function u(e){try{return o.default.createHash(`sha256`).update(s.default.readFileSync(e)).digest(`hex`)}catch{return}}function d(e){let t;try{t=s.default.statSync(e)}catch{return}if(!t.isFile())return;let n=u(e);if(n!==void 0)return{path:e,size:t.size,mtimeMs:t.mtimeMs,sha256:n}}function f(e){if(m(e)&&typeof e.path==`string`&&typeof e.size==`number`&&typeof e.mtimeMs==`number`&&typeof e.sha256==`string`)return{path:e.path,size:e.size,mtimeMs:e.mtimeMs,sha256:e.sha256}}function p(e){let t=[],n=o.default.createHash(`sha256`);for(let r of e){let e;try{e=s.default.statSync(r.path)}catch{return!1}if(!e.isFile())return!1;n.update(`${r.path} ${String(e.size)} ${String(e.mtimeMs)} `),(e.size!==r.size||e.mtimeMs!==r.mtimeMs)&&t.push(r)}if(t.length===0)return!0;let r=n.digest(`hex`),i=l.get(r);if(i!==void 0)return i;let a=t.every(e=>u(e.path)===e.sha256);return l.set(r,a),a}function m(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function h(e){return m(e)?Object.fromEntries(Object.entries(e).filter(e=>typeof e[1]==`string`)):{}}function g(e){let t=a.default.resolve(e);try{return s.default.realpathSync(t)}catch{let e=a.default.dirname(t);return e===t?t:a.default.join(g(e),a.default.basename(t))}}function _(e,t){let n=a.default.relative(g(e),g(t));return n===``||!n.startsWith(`..`)&&!a.default.isAbsolute(n)}function v(e=require("url").pathToFileURL(__filename).href){let t=process.env.DOOMPI_BOOTSTRAP_ENTRY;if(t)return t;let n=e.endsWith(`.ts`)?`ts`:`mjs`,r=a.default.dirname(a.default.dirname((0,i.fileURLToPath)(e)));return a.default.join(r,`extensions`,`entries`,`doom.${n}`)}function y(e,t){return n.readSyncRegistration(e,t)!==void 0}function b(e,n=c.default.homedir()){if(y(e,n))return g(e);let r=t.resolveDoomConfigurationRoot(e,n);return y(r,n)?g(r):void 0}function x(e,t=c.default.homedir()){let i=n.readSyncRegistration(e,t);if(!i)return;let a=i.statePath,o=i.generationRoot,s=r.readSyncState(e,t);if(!s)return;let l=typeof s.bootstrap==`string`?s.bootstrap:void 0;if(l&&!_(o,l))throw Error(`Doom bootstrap must stay inside ${o}: ${l}`);let u;if(s.precompile!==void 0){let e=s.precompile;if(!m(e)||typeof e.version!=`number`||e.strategy!==`bundle`||typeof e.bootstrapEntry!=`string`||typeof e.bootstrapManifest!=`string`||!m(e.bundleManifests)||Object.values(e.bundleManifests).some(e=>typeof e!=`string`))throw Error(`Doom sync state at ${a} has an invalid precompile record`);u={version:e.version,strategy:e.strategy,bootstrapEntry:e.bootstrapEntry,bootstrapManifest:e.bootstrapManifest,bundleManifests:e.bundleManifests}}return{statePath:a,generatedDirectory:o,bootstrap:l,bundles:h(s.bundles),precompile:u}}function S(e,t){if(_(t,e))try{let n=JSON.parse(s.default.readFileSync(e,`utf8`));if(!m(n)||typeof n.output!=`string`||!Array.isArray(n.artifacts)||n.artifacts.some(e=>typeof e!=`string`)||!Array.isArray(n.entries)||n.entries.some(e=>typeof e!=`string`)||!Array.isArray(n.inputs))return;let r=[];for(let e of n.inputs){let t=f(e);if(t===void 0)return;r.push(t)}let i=n.artifacts,a=n.entries;return!_(t,n.output)||i.some(e=>!_(t,e))?void 0:{output:n.output,artifacts:i,entries:a,inputs:r}}catch{return}}function C(e){return!s.default.existsSync(e.output)||e.artifacts.some(e=>!s.default.existsSync(e))?!1:p(e.inputs)}function w(e,t){if(!e.bootstrap||!e.precompile||e.precompile.version!==5||g(e.precompile.bootstrapEntry)!==g(t))return;let n=S(e.precompile.bootstrapManifest,e.generatedDirectory);if(!(!n||!C(n)||n.output!==e.bootstrap||n.entries.length!==1||g(n.entries[0]??``)!==g(t)))return n}function T(e,t,n=c.default.homedir()){let r=x(e,n),i=t??v();return r?{bootstrap:r.bootstrap,fresh:w(r,i)!==void 0}:{bootstrap:void 0,fresh:!1}}function E(e,t,n=c.default.homedir()){let r=x(e,n),i=r?.bundles[t],a=r?.precompile?.bundleManifests[t];if(!r||r.precompile?.version!==5||!i||!a)return{bundle:i,fresh:!1};let o=S(a,r.generatedDirectory);return{bundle:i,fresh:!!(o&&o.output===i&&C(o))}}function D(e,t,n=c.default.homedir()){let r=x(e,n),i=t??v();if(!r?.bootstrap||!r.precompile||!w(r,i))return{bootstrap:r?.bootstrap,fresh:!1};let a=Object.values(r.precompile.bundleManifests);if(new Set(a).size!==a.length)return{bootstrap:r.bootstrap,fresh:!1};let o=Object.entries(r.bundles).every(([e,t])=>{let n=r.precompile?.bundleManifests[e];if(!n)return!1;let i=S(n,r.generatedDirectory);return!!(i&&i.output===t&&C(i))});return{bootstrap:r.bootstrap,fresh:o}}exports.findSyncedRoot=b,exports.fingerprintInput=d,exports.inputsAreFresh=p,exports.parseInputFingerprint=f,exports.readBootstrapStatus=D,exports.readBundleStatus=E,exports.readStartupBootstrapStatus=T;
|
|
2
2
|
//# sourceMappingURL=bootstrapLocator.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapLocator.cjs","names":["crypto","fs","path","fileURLToPath","readSyncRegistration","os","resolveDoomConfigurationRoot","readSyncState"],"sources":["../../../src/adapters/bootstrapLocator.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveDoomConfigurationRoot } from './repository/repository.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { readSyncState } from './syncState.ts';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from './syncStateContract.ts';\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, so a rebuild that rewrote every input without\n * changing it does not re-hash the whole graph on every poll.\n *\n * Keyed by the stats actually found on disk, so it can never answer for a set\n * it did not see. Bounded by the compiler manifests in one composition, which\n * is single digits.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(`${input.path} ${String(stat.size)} ${String(stat.mtimeMs)} `);\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n}\n\ninterface BootstrapState {\n statePath: string;\n generatedDirectory: string;\n bootstrap?: string;\n bundles: Record<string, string>;\n precompile?: {\n version: number;\n strategy: 'bundle';\n bootstrapEntry: string;\n bootstrapManifest: string;\n bundleManifests: Record<string, string>;\n };\n}\n\nexport interface BootstrapStatus {\n bootstrap?: string;\n fresh: boolean;\n}\n\nexport interface BundleStatus {\n bundle?: string;\n fresh: boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction stringRecord(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packagedDoomEntry(moduleUrl: string = import.meta.url): string {\n const extension = moduleUrl.endsWith('.ts') ? 'ts' : 'mjs';\n const sourceRoot = path.dirname(path.dirname(fileURLToPath(moduleUrl)));\n return path.join(sourceRoot, 'extensions', 'entries', `doom.${extension}`);\n}\n\nfunction locationHasState(repoRoot: string, homeDirectory: string): boolean {\n return readSyncRegistration(repoRoot, homeDirectory) !== undefined;\n}\n\n/** Finds the nearest configured repository with generated Doom sync state. */\nexport function findSyncedRoot(cwd: string, homeDirectory: string = os.homedir()): string | undefined {\n if (locationHasState(cwd, homeDirectory)) return canonicalPath(cwd);\n const root = resolveDoomConfigurationRoot(cwd, homeDirectory);\n return locationHasState(root, homeDirectory) ? canonicalPath(root) : undefined;\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readSyncState(repoRoot, homeDirectory);\n if (!state) return undefined;\n const bootstrap = typeof state.bootstrap === 'string' ? state.bootstrap : undefined;\n if (bootstrap && !isInside(generatedDirectory, bootstrap)) {\n throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);\n }\n\n let precompile: BootstrapState['precompile'];\n if (state.precompile !== undefined) {\n const value = state.precompile;\n if (\n !isRecord(value) ||\n typeof value.version !== 'number' ||\n value.strategy !== BUNDLED_PRECOMPILE_STRATEGY ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.values(value.bundleManifests).some((manifest) => typeof manifest !== 'string')\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n precompile = {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: stringRecord(state.bundles),\n precompile,\n };\n}\n\nfunction readCompilerManifest(manifestPath: string, generatedDirectory: string): CompilerManifest | undefined {\n if (!isInside(generatedDirectory, manifestPath)) return undefined;\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (\n !isRecord(parsed) ||\n typeof parsed.output !== 'string' ||\n !Array.isArray(parsed.artifacts) ||\n parsed.artifacts.some((artifact) => typeof artifact !== 'string') ||\n !Array.isArray(parsed.entries) ||\n parsed.entries.some((entry) => typeof entry !== 'string') ||\n !Array.isArray(parsed.inputs)\n ) {\n return undefined;\n }\n const inputs: InputFingerprint[] = [];\n for (const input of parsed.inputs) {\n // A manifest written before digests were recorded cannot be confirmed by\n // content, so it reads as absent and the next sync rewrites it.\n const fingerprint = parseInputFingerprint(input);\n if (fingerprint === undefined) return undefined;\n inputs.push(fingerprint);\n }\n const artifacts = parsed.artifacts as string[];\n const entries = parsed.entries as string[];\n if (\n !isInside(generatedDirectory, parsed.output) ||\n artifacts.some((artifact) => !isInside(generatedDirectory, artifact))\n ) {\n return undefined;\n }\n return { output: parsed.output, artifacts, entries, inputs };\n } catch {\n return undefined;\n }\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest): boolean {\n if (!fs.existsSync(manifest.output) || manifest.artifacts.some((artifact) => !fs.existsSync(artifact))) return false;\n return inputsAreFresh(manifest.inputs);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n if (!state.bootstrap || !state.precompile) return undefined;\n if (\n state.precompile.version !== PRECOMPILE_STATE_VERSION ||\n canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);\n if (\n !record ||\n !compilerManifestIsFresh(record) ||\n record.output !== state.bootstrap ||\n record.entries.length !== 1 ||\n canonicalPath(record.entries[0] ?? '') !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n return record;\n}\n\n/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */\nexport function readStartupBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state) return { bootstrap: undefined, fresh: false };\n return {\n bootstrap: state.bootstrap,\n fresh: freshBootstrapRecord(state, expected) !== undefined,\n };\n}\n\n/** Validates one selected composition without touching any inactive bundle inputs. */\nexport function readBundleStatus(\n repoRoot: string,\n compositionFingerprint: string,\n homeDirectory: string = os.homedir(),\n): BundleStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const bundle = state?.bundles[compositionFingerprint];\n const manifestPath = state?.precompile?.bundleManifests[compositionFingerprint];\n if (!state || state.precompile?.version !== PRECOMPILE_STATE_VERSION || !bundle || !manifestPath) {\n return { bundle, fresh: false };\n }\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return {\n bundle,\n fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest)),\n };\n}\n\n/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */\nexport function readBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state?.bootstrap || !state.precompile || !freshBootstrapRecord(state, expected)) {\n return { bootstrap: state?.bootstrap, fresh: false };\n }\n const manifests = Object.values(state.precompile.bundleManifests);\n if (new Set(manifests).size !== manifests.length) return { bootstrap: state.bootstrap, fresh: false };\n const fresh = Object.entries(state.bundles).every(([fingerprint, bundle]) => {\n const manifestPath = state.precompile?.bundleManifests[fingerprint];\n if (!manifestPath) return false;\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));\n });\n return { bootstrap: state.bootstrap, fresh };\n}\n\n/** Reads only the generated bootstrap pointer while retaining state validation. */\nexport function readBootstrapPointer(repoRoot: string, homeDirectory: string = os.homedir()): string | undefined {\n return readBootstrapState(repoRoot, homeDirectory)?.bootstrap;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bootstrapLocator.cjs","names":["crypto","fs","path","fileURLToPath","readSyncRegistration","os","resolveDoomConfigurationRoot","readSyncState"],"sources":["../../../src/adapters/bootstrapLocator.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveDoomConfigurationRoot } from './repository/repository.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { readSyncState } from './syncState.ts';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from './syncStateContract.ts';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, so a rebuild that rewrote every input without\n * changing it does not re-hash the whole graph on every poll.\n *\n * Keyed by the stats actually found on disk, so it can never answer for a set\n * it did not see. Bounded by the compiler manifests in one composition, which\n * is single digits.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(`${input.path} ${String(stat.size)} ${String(stat.mtimeMs)} `);\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n}\n\ninterface BootstrapState {\n statePath: string;\n generatedDirectory: string;\n bootstrap?: string;\n bundles: Record<string, string>;\n precompile?: {\n version: number;\n strategy: 'bundle';\n bootstrapEntry: string;\n bootstrapManifest: string;\n bundleManifests: Record<string, string>;\n };\n}\n\nexport interface BootstrapStatus {\n bootstrap?: string;\n fresh: boolean;\n}\n\nexport interface BundleStatus {\n bundle?: string;\n fresh: boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction stringRecord(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packagedDoomEntry(moduleUrl: string = import.meta.url): string {\n const configuredEntry = process.env[BOOTSTRAP_ENTRY_ENV];\n if (configuredEntry) return configuredEntry;\n const extension = moduleUrl.endsWith('.ts') ? 'ts' : 'mjs';\n const sourceRoot = path.dirname(path.dirname(fileURLToPath(moduleUrl)));\n return path.join(sourceRoot, 'extensions', 'entries', `doom.${extension}`);\n}\n\nfunction locationHasState(repoRoot: string, homeDirectory: string): boolean {\n return readSyncRegistration(repoRoot, homeDirectory) !== undefined;\n}\n\n/** Finds the nearest configured repository with generated Doom sync state. */\nexport function findSyncedRoot(cwd: string, homeDirectory: string = os.homedir()): string | undefined {\n if (locationHasState(cwd, homeDirectory)) return canonicalPath(cwd);\n const root = resolveDoomConfigurationRoot(cwd, homeDirectory);\n return locationHasState(root, homeDirectory) ? canonicalPath(root) : undefined;\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readSyncState(repoRoot, homeDirectory);\n if (!state) return undefined;\n const bootstrap = typeof state.bootstrap === 'string' ? state.bootstrap : undefined;\n if (bootstrap && !isInside(generatedDirectory, bootstrap)) {\n throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);\n }\n\n let precompile: BootstrapState['precompile'];\n if (state.precompile !== undefined) {\n const value = state.precompile;\n if (\n !isRecord(value) ||\n typeof value.version !== 'number' ||\n value.strategy !== BUNDLED_PRECOMPILE_STRATEGY ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.values(value.bundleManifests).some((manifest) => typeof manifest !== 'string')\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n precompile = {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: stringRecord(state.bundles),\n precompile,\n };\n}\n\nfunction readCompilerManifest(manifestPath: string, generatedDirectory: string): CompilerManifest | undefined {\n if (!isInside(generatedDirectory, manifestPath)) return undefined;\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (\n !isRecord(parsed) ||\n typeof parsed.output !== 'string' ||\n !Array.isArray(parsed.artifacts) ||\n parsed.artifacts.some((artifact) => typeof artifact !== 'string') ||\n !Array.isArray(parsed.entries) ||\n parsed.entries.some((entry) => typeof entry !== 'string') ||\n !Array.isArray(parsed.inputs)\n ) {\n return undefined;\n }\n const inputs: InputFingerprint[] = [];\n for (const input of parsed.inputs) {\n // A manifest written before digests were recorded cannot be confirmed by\n // content, so it reads as absent and the next sync rewrites it.\n const fingerprint = parseInputFingerprint(input);\n if (fingerprint === undefined) return undefined;\n inputs.push(fingerprint);\n }\n const artifacts = parsed.artifacts as string[];\n const entries = parsed.entries as string[];\n if (\n !isInside(generatedDirectory, parsed.output) ||\n artifacts.some((artifact) => !isInside(generatedDirectory, artifact))\n ) {\n return undefined;\n }\n return { output: parsed.output, artifacts, entries, inputs };\n } catch {\n return undefined;\n }\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest): boolean {\n if (!fs.existsSync(manifest.output) || manifest.artifacts.some((artifact) => !fs.existsSync(artifact))) return false;\n return inputsAreFresh(manifest.inputs);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n if (!state.bootstrap || !state.precompile) return undefined;\n if (\n state.precompile.version !== PRECOMPILE_STATE_VERSION ||\n canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);\n if (\n !record ||\n !compilerManifestIsFresh(record) ||\n record.output !== state.bootstrap ||\n record.entries.length !== 1 ||\n canonicalPath(record.entries[0] ?? '') !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n return record;\n}\n\n/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */\nexport function readStartupBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state) return { bootstrap: undefined, fresh: false };\n return {\n bootstrap: state.bootstrap,\n fresh: freshBootstrapRecord(state, expected) !== undefined,\n };\n}\n\n/** Validates one selected composition without touching any inactive bundle inputs. */\nexport function readBundleStatus(\n repoRoot: string,\n compositionFingerprint: string,\n homeDirectory: string = os.homedir(),\n): BundleStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const bundle = state?.bundles[compositionFingerprint];\n const manifestPath = state?.precompile?.bundleManifests[compositionFingerprint];\n if (!state || state.precompile?.version !== PRECOMPILE_STATE_VERSION || !bundle || !manifestPath) {\n return { bundle, fresh: false };\n }\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return {\n bundle,\n fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest)),\n };\n}\n\n/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */\nexport function readBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state?.bootstrap || !state.precompile || !freshBootstrapRecord(state, expected)) {\n return { bootstrap: state?.bootstrap, fresh: false };\n }\n const manifests = Object.values(state.precompile.bundleManifests);\n if (new Set(manifests).size !== manifests.length) return { bootstrap: state.bootstrap, fresh: false };\n const fresh = Object.entries(state.bundles).every(([fingerprint, bundle]) => {\n const manifestPath = state.precompile?.bundleManifests[fingerprint];\n if (!manifestPath) return false;\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));\n });\n return { bootstrap: state.bootstrap, fresh };\n}\n\n/** Reads only the generated bootstrap pointer while retaining state validation. */\nexport function readBootstrapPointer(repoRoot: string, homeDirectory: string = os.homedir()): string | undefined {\n return readBootstrapState(repoRoot, homeDirectory)?.bootstrap;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapLocator.d.cts","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"bootstrapLocator.d.cts","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"mappings":";;iBAuLgB,eAAe,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapLocator.d.mts","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"bootstrapLocator.d.mts","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"mappings":";;iBAuLgB,eAAe,aAAa"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{resolveDoomConfigurationRoot as e}from"./repository/repository.mjs";import{readSyncRegistration as t}from"./syncRegistration.mjs";import"./syncStateContract.mjs";import{readSyncState as n}from"./syncState.mjs";import{fileURLToPath as r}from"node:url";import i from"node:path";import a from"node:crypto";import o from"node:fs";import s from"node:os";const c=new Map;function l(e){try{return a.createHash(`sha256`).update(o.readFileSync(e)).digest(`hex`)}catch{return}}function u(e){let t;try{t=o.statSync(e)}catch{return}if(!t.isFile())return;let n=l(e);if(n!==void 0)return{path:e,size:t.size,mtimeMs:t.mtimeMs,sha256:n}}function d(e){if(p(e)&&typeof e.path==`string`&&typeof e.size==`number`&&typeof e.mtimeMs==`number`&&typeof e.sha256==`string`)return{path:e.path,size:e.size,mtimeMs:e.mtimeMs,sha256:e.sha256}}function f(e){let t=[],n=a.createHash(`sha256`);for(let r of e){let e;try{e=o.statSync(r.path)}catch{return!1}if(!e.isFile())return!1;n.update(`${r.path} ${String(e.size)} ${String(e.mtimeMs)} `),(e.size!==r.size||e.mtimeMs!==r.mtimeMs)&&t.push(r)}if(t.length===0)return!0;let r=n.digest(`hex`),i=c.get(r);if(i!==void 0)return i;let s=t.every(e=>l(e.path)===e.sha256);return c.set(r,s),s}function p(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function m(e){return p(e)?Object.fromEntries(Object.entries(e).filter(e=>typeof e[1]==`string`)):{}}function h(e){let t=i.resolve(e);try{return o.realpathSync(t)}catch{let e=i.dirname(t);return e===t?t:i.join(h(e),i.basename(t))}}function g(e,t){let n=i.relative(h(e),h(t));return n===``||!n.startsWith(`..`)&&!i.isAbsolute(n)}function _(e=import.meta.url){let t=e.endsWith(`.ts`)?`ts`:`mjs`,
|
|
1
|
+
import{resolveDoomConfigurationRoot as e}from"./repository/repository.mjs";import{readSyncRegistration as t}from"./syncRegistration.mjs";import"./syncStateContract.mjs";import{readSyncState as n}from"./syncState.mjs";import{fileURLToPath as r}from"node:url";import i from"node:path";import a from"node:crypto";import o from"node:fs";import s from"node:os";const c=new Map;function l(e){try{return a.createHash(`sha256`).update(o.readFileSync(e)).digest(`hex`)}catch{return}}function u(e){let t;try{t=o.statSync(e)}catch{return}if(!t.isFile())return;let n=l(e);if(n!==void 0)return{path:e,size:t.size,mtimeMs:t.mtimeMs,sha256:n}}function d(e){if(p(e)&&typeof e.path==`string`&&typeof e.size==`number`&&typeof e.mtimeMs==`number`&&typeof e.sha256==`string`)return{path:e.path,size:e.size,mtimeMs:e.mtimeMs,sha256:e.sha256}}function f(e){let t=[],n=a.createHash(`sha256`);for(let r of e){let e;try{e=o.statSync(r.path)}catch{return!1}if(!e.isFile())return!1;n.update(`${r.path} ${String(e.size)} ${String(e.mtimeMs)} `),(e.size!==r.size||e.mtimeMs!==r.mtimeMs)&&t.push(r)}if(t.length===0)return!0;let r=n.digest(`hex`),i=c.get(r);if(i!==void 0)return i;let s=t.every(e=>l(e.path)===e.sha256);return c.set(r,s),s}function p(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function m(e){return p(e)?Object.fromEntries(Object.entries(e).filter(e=>typeof e[1]==`string`)):{}}function h(e){let t=i.resolve(e);try{return o.realpathSync(t)}catch{let e=i.dirname(t);return e===t?t:i.join(h(e),i.basename(t))}}function g(e,t){let n=i.relative(h(e),h(t));return n===``||!n.startsWith(`..`)&&!i.isAbsolute(n)}function _(e=import.meta.url){let t=process.env.DOOMPI_BOOTSTRAP_ENTRY;if(t)return t;let n=e.endsWith(`.ts`)?`ts`:`mjs`,a=i.dirname(i.dirname(r(e)));return i.join(a,`extensions`,`entries`,`doom.${n}`)}function v(e,n){return t(e,n)!==void 0}function y(t,n=s.homedir()){if(v(t,n))return h(t);let r=e(t,n);return v(r,n)?h(r):void 0}function b(e,r=s.homedir()){let i=t(e,r);if(!i)return;let a=i.statePath,o=i.generationRoot,c=n(e,r);if(!c)return;let l=typeof c.bootstrap==`string`?c.bootstrap:void 0;if(l&&!g(o,l))throw Error(`Doom bootstrap must stay inside ${o}: ${l}`);let u;if(c.precompile!==void 0){let e=c.precompile;if(!p(e)||typeof e.version!=`number`||e.strategy!==`bundle`||typeof e.bootstrapEntry!=`string`||typeof e.bootstrapManifest!=`string`||!p(e.bundleManifests)||Object.values(e.bundleManifests).some(e=>typeof e!=`string`))throw Error(`Doom sync state at ${a} has an invalid precompile record`);u={version:e.version,strategy:e.strategy,bootstrapEntry:e.bootstrapEntry,bootstrapManifest:e.bootstrapManifest,bundleManifests:e.bundleManifests}}return{statePath:a,generatedDirectory:o,bootstrap:l,bundles:m(c.bundles),precompile:u}}function x(e,t){if(g(t,e))try{let n=JSON.parse(o.readFileSync(e,`utf8`));if(!p(n)||typeof n.output!=`string`||!Array.isArray(n.artifacts)||n.artifacts.some(e=>typeof e!=`string`)||!Array.isArray(n.entries)||n.entries.some(e=>typeof e!=`string`)||!Array.isArray(n.inputs))return;let r=[];for(let e of n.inputs){let t=d(e);if(t===void 0)return;r.push(t)}let i=n.artifacts,a=n.entries;return!g(t,n.output)||i.some(e=>!g(t,e))?void 0:{output:n.output,artifacts:i,entries:a,inputs:r}}catch{return}}function S(e){return!o.existsSync(e.output)||e.artifacts.some(e=>!o.existsSync(e))?!1:f(e.inputs)}function C(e,t){if(!e.bootstrap||!e.precompile||e.precompile.version!==5||h(e.precompile.bootstrapEntry)!==h(t))return;let n=x(e.precompile.bootstrapManifest,e.generatedDirectory);if(!(!n||!S(n)||n.output!==e.bootstrap||n.entries.length!==1||h(n.entries[0]??``)!==h(t)))return n}function w(e,t,n=s.homedir()){let r=b(e,n),i=t??_();return r?{bootstrap:r.bootstrap,fresh:C(r,i)!==void 0}:{bootstrap:void 0,fresh:!1}}function T(e,t,n=s.homedir()){let r=b(e,n),i=r?.bundles[t],a=r?.precompile?.bundleManifests[t];if(!r||r.precompile?.version!==5||!i||!a)return{bundle:i,fresh:!1};let o=x(a,r.generatedDirectory);return{bundle:i,fresh:!!(o&&o.output===i&&S(o))}}function E(e,t,n=s.homedir()){let r=b(e,n),i=t??_();if(!r?.bootstrap||!r.precompile||!C(r,i))return{bootstrap:r?.bootstrap,fresh:!1};let a=Object.values(r.precompile.bundleManifests);if(new Set(a).size!==a.length)return{bootstrap:r.bootstrap,fresh:!1};let o=Object.entries(r.bundles).every(([e,t])=>{let n=r.precompile?.bundleManifests[e];if(!n)return!1;let i=x(n,r.generatedDirectory);return!!(i&&i.output===t&&S(i))});return{bootstrap:r.bootstrap,fresh:o}}export{y as findSyncedRoot,u as fingerprintInput,f as inputsAreFresh,d as parseInputFingerprint,E as readBootstrapStatus,T as readBundleStatus,w as readStartupBootstrapStatus};
|
|
2
2
|
//# sourceMappingURL=bootstrapLocator.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bootstrapLocator.mjs","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveDoomConfigurationRoot } from './repository/repository.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { readSyncState } from './syncState.ts';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from './syncStateContract.ts';\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, so a rebuild that rewrote every input without\n * changing it does not re-hash the whole graph on every poll.\n *\n * Keyed by the stats actually found on disk, so it can never answer for a set\n * it did not see. Bounded by the compiler manifests in one composition, which\n * is single digits.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(`${input.path} ${String(stat.size)} ${String(stat.mtimeMs)} `);\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n}\n\ninterface BootstrapState {\n statePath: string;\n generatedDirectory: string;\n bootstrap?: string;\n bundles: Record<string, string>;\n precompile?: {\n version: number;\n strategy: 'bundle';\n bootstrapEntry: string;\n bootstrapManifest: string;\n bundleManifests: Record<string, string>;\n };\n}\n\nexport interface BootstrapStatus {\n bootstrap?: string;\n fresh: boolean;\n}\n\nexport interface BundleStatus {\n bundle?: string;\n fresh: boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction stringRecord(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packagedDoomEntry(moduleUrl: string = import.meta.url): string {\n const extension = moduleUrl.endsWith('.ts') ? 'ts' : 'mjs';\n const sourceRoot = path.dirname(path.dirname(fileURLToPath(moduleUrl)));\n return path.join(sourceRoot, 'extensions', 'entries', `doom.${extension}`);\n}\n\nfunction locationHasState(repoRoot: string, homeDirectory: string): boolean {\n return readSyncRegistration(repoRoot, homeDirectory) !== undefined;\n}\n\n/** Finds the nearest configured repository with generated Doom sync state. */\nexport function findSyncedRoot(cwd: string, homeDirectory: string = os.homedir()): string | undefined {\n if (locationHasState(cwd, homeDirectory)) return canonicalPath(cwd);\n const root = resolveDoomConfigurationRoot(cwd, homeDirectory);\n return locationHasState(root, homeDirectory) ? canonicalPath(root) : undefined;\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readSyncState(repoRoot, homeDirectory);\n if (!state) return undefined;\n const bootstrap = typeof state.bootstrap === 'string' ? state.bootstrap : undefined;\n if (bootstrap && !isInside(generatedDirectory, bootstrap)) {\n throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);\n }\n\n let precompile: BootstrapState['precompile'];\n if (state.precompile !== undefined) {\n const value = state.precompile;\n if (\n !isRecord(value) ||\n typeof value.version !== 'number' ||\n value.strategy !== BUNDLED_PRECOMPILE_STRATEGY ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.values(value.bundleManifests).some((manifest) => typeof manifest !== 'string')\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n precompile = {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: stringRecord(state.bundles),\n precompile,\n };\n}\n\nfunction readCompilerManifest(manifestPath: string, generatedDirectory: string): CompilerManifest | undefined {\n if (!isInside(generatedDirectory, manifestPath)) return undefined;\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (\n !isRecord(parsed) ||\n typeof parsed.output !== 'string' ||\n !Array.isArray(parsed.artifacts) ||\n parsed.artifacts.some((artifact) => typeof artifact !== 'string') ||\n !Array.isArray(parsed.entries) ||\n parsed.entries.some((entry) => typeof entry !== 'string') ||\n !Array.isArray(parsed.inputs)\n ) {\n return undefined;\n }\n const inputs: InputFingerprint[] = [];\n for (const input of parsed.inputs) {\n // A manifest written before digests were recorded cannot be confirmed by\n // content, so it reads as absent and the next sync rewrites it.\n const fingerprint = parseInputFingerprint(input);\n if (fingerprint === undefined) return undefined;\n inputs.push(fingerprint);\n }\n const artifacts = parsed.artifacts as string[];\n const entries = parsed.entries as string[];\n if (\n !isInside(generatedDirectory, parsed.output) ||\n artifacts.some((artifact) => !isInside(generatedDirectory, artifact))\n ) {\n return undefined;\n }\n return { output: parsed.output, artifacts, entries, inputs };\n } catch {\n return undefined;\n }\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest): boolean {\n if (!fs.existsSync(manifest.output) || manifest.artifacts.some((artifact) => !fs.existsSync(artifact))) return false;\n return inputsAreFresh(manifest.inputs);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n if (!state.bootstrap || !state.precompile) return undefined;\n if (\n state.precompile.version !== PRECOMPILE_STATE_VERSION ||\n canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);\n if (\n !record ||\n !compilerManifestIsFresh(record) ||\n record.output !== state.bootstrap ||\n record.entries.length !== 1 ||\n canonicalPath(record.entries[0] ?? '') !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n return record;\n}\n\n/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */\nexport function readStartupBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state) return { bootstrap: undefined, fresh: false };\n return {\n bootstrap: state.bootstrap,\n fresh: freshBootstrapRecord(state, expected) !== undefined,\n };\n}\n\n/** Validates one selected composition without touching any inactive bundle inputs. */\nexport function readBundleStatus(\n repoRoot: string,\n compositionFingerprint: string,\n homeDirectory: string = os.homedir(),\n): BundleStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const bundle = state?.bundles[compositionFingerprint];\n const manifestPath = state?.precompile?.bundleManifests[compositionFingerprint];\n if (!state || state.precompile?.version !== PRECOMPILE_STATE_VERSION || !bundle || !manifestPath) {\n return { bundle, fresh: false };\n }\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return {\n bundle,\n fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest)),\n };\n}\n\n/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */\nexport function readBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state?.bootstrap || !state.precompile || !freshBootstrapRecord(state, expected)) {\n return { bootstrap: state?.bootstrap, fresh: false };\n }\n const manifests = Object.values(state.precompile.bundleManifests);\n if (new Set(manifests).size !== manifests.length) return { bootstrap: state.bootstrap, fresh: false };\n const fresh = Object.entries(state.bundles).every(([fingerprint, bundle]) => {\n const manifestPath = state.precompile?.bundleManifests[fingerprint];\n if (!manifestPath) return false;\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));\n });\n return { bootstrap: state.bootstrap, fresh };\n}\n\n/** Reads only the generated bootstrap pointer while retaining state validation. */\nexport function readBootstrapPointer(repoRoot: string, homeDirectory: string = os.homedir()): string | undefined {\n return readBootstrapState(repoRoot, homeDirectory)?.bootstrap;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bootstrapLocator.mjs","names":[],"sources":["../../../src/adapters/bootstrapLocator.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { fileURLToPath } from 'node:url';\nimport { resolveDoomConfigurationRoot } from './repository/repository.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\nimport { readSyncState } from './syncState.ts';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from './syncStateContract.ts';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\n\n/**\n * One recorded build input: the stat pair for speed, the digest for truth.\n *\n * Comparing only `size` and `mtimeMs` makes every rebuild look like a change,\n * including one that restored byte-identical output from a build cache, and a\n * sync triggered that way republishes a generation nobody asked for. Comparing\n * content alone is correct but reads every input on every check, and the\n * cockpit polls this. Recording both lets the stat pair answer the common\n * \"nothing was touched\" case without opening a file, while the digest stays the\n * authority for anything whose stat moved.\n */\nexport interface InputFingerprint {\n path: string;\n size: number;\n mtimeMs: number;\n sha256: string;\n}\n\n/**\n * Verdicts already reached, so a rebuild that rewrote every input without\n * changing it does not re-hash the whole graph on every poll.\n *\n * Keyed by the stats actually found on disk, so it can never answer for a set\n * it did not see. Bounded by the compiler manifests in one composition, which\n * is single digits.\n */\nconst freshnessVerdicts = new Map<string, boolean>();\n\nfunction digestOf(filePath: string): string | undefined {\n try {\n return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex');\n } catch {\n // Unreadable is indistinguishable from absent here: either way the recorded\n // digest cannot be confirmed, so the caller must treat the build as stale.\n return undefined;\n }\n}\n\n/** Records one build input, or undefined when it is not a readable regular file. */\nexport function fingerprintInput(target: string): InputFingerprint | undefined {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(target);\n } catch {\n // A path that vanished between discovery and recording is simply not an\n // input; the compiler records the set it could read.\n return undefined;\n }\n if (!stat.isFile()) return undefined;\n const sha256 = digestOf(target);\n if (sha256 === undefined) return undefined;\n return { path: target, size: stat.size, mtimeMs: stat.mtimeMs, sha256 };\n}\n\n/** Parses one recorded input, rejecting a record written before digests existed. */\nexport function parseInputFingerprint(value: unknown): InputFingerprint | undefined {\n if (!isRecord(value)) return undefined;\n if (\n typeof value.path !== 'string' ||\n typeof value.size !== 'number' ||\n typeof value.mtimeMs !== 'number' ||\n typeof value.sha256 !== 'string'\n ) {\n return undefined;\n }\n return { path: value.path, size: value.size, mtimeMs: value.mtimeMs, sha256: value.sha256 };\n}\n\n/** Whether every recorded input still holds the bytes it was recorded with. */\nexport function inputsAreFresh(inputs: readonly InputFingerprint[]): boolean {\n const moved: InputFingerprint[] = [];\n const key = crypto.createHash('sha256');\n for (const input of inputs) {\n let stat: fs.Stats;\n try {\n stat = fs.statSync(input.path);\n } catch {\n // A recorded input that is gone cannot be confirmed, and no digest will\n // bring it back; the build it describes is stale.\n return false;\n }\n if (!stat.isFile()) return false;\n key.update(`${input.path} ${String(stat.size)} ${String(stat.mtimeMs)} `);\n if (stat.size !== input.size || stat.mtimeMs !== input.mtimeMs) moved.push(input);\n }\n if (moved.length === 0) return true;\n\n const cacheKey = key.digest('hex');\n const remembered = freshnessVerdicts.get(cacheKey);\n if (remembered !== undefined) return remembered;\n const fresh = moved.every((input) => digestOf(input.path) === input.sha256);\n freshnessVerdicts.set(cacheKey, fresh);\n return fresh;\n}\n\n/** Test seam: the memo is process-local and must not leak between cases. */\nexport function resetInputFreshnessCache(): void {\n freshnessVerdicts.clear();\n}\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n}\n\ninterface BootstrapState {\n statePath: string;\n generatedDirectory: string;\n bootstrap?: string;\n bundles: Record<string, string>;\n precompile?: {\n version: number;\n strategy: 'bundle';\n bootstrapEntry: string;\n bootstrapManifest: string;\n bundleManifests: Record<string, string>;\n };\n}\n\nexport interface BootstrapStatus {\n bootstrap?: string;\n fresh: boolean;\n}\n\nexport interface BundleStatus {\n bundle?: string;\n fresh: boolean;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction stringRecord(value: unknown): Record<string, string> {\n if (!isRecord(value)) return {};\n return Object.fromEntries(\n Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n}\n\nfunction canonicalPath(target: string): string {\n const absolute = path.resolve(target);\n try {\n return fs.realpathSync(absolute);\n } catch {\n const parent = path.dirname(absolute);\n if (parent === absolute) return absolute;\n return path.join(canonicalPath(parent), path.basename(absolute));\n }\n}\n\nfunction isInside(directory: string, target: string): boolean {\n const relative = path.relative(canonicalPath(directory), canonicalPath(target));\n return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative));\n}\n\nfunction packagedDoomEntry(moduleUrl: string = import.meta.url): string {\n const configuredEntry = process.env[BOOTSTRAP_ENTRY_ENV];\n if (configuredEntry) return configuredEntry;\n const extension = moduleUrl.endsWith('.ts') ? 'ts' : 'mjs';\n const sourceRoot = path.dirname(path.dirname(fileURLToPath(moduleUrl)));\n return path.join(sourceRoot, 'extensions', 'entries', `doom.${extension}`);\n}\n\nfunction locationHasState(repoRoot: string, homeDirectory: string): boolean {\n return readSyncRegistration(repoRoot, homeDirectory) !== undefined;\n}\n\n/** Finds the nearest configured repository with generated Doom sync state. */\nexport function findSyncedRoot(cwd: string, homeDirectory: string = os.homedir()): string | undefined {\n if (locationHasState(cwd, homeDirectory)) return canonicalPath(cwd);\n const root = resolveDoomConfigurationRoot(cwd, homeDirectory);\n return locationHasState(root, homeDirectory) ? canonicalPath(root) : undefined;\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n if (!registration) return undefined;\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readSyncState(repoRoot, homeDirectory);\n if (!state) return undefined;\n const bootstrap = typeof state.bootstrap === 'string' ? state.bootstrap : undefined;\n if (bootstrap && !isInside(generatedDirectory, bootstrap)) {\n throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);\n }\n\n let precompile: BootstrapState['precompile'];\n if (state.precompile !== undefined) {\n const value = state.precompile;\n if (\n !isRecord(value) ||\n typeof value.version !== 'number' ||\n value.strategy !== BUNDLED_PRECOMPILE_STRATEGY ||\n typeof value.bootstrapEntry !== 'string' ||\n typeof value.bootstrapManifest !== 'string' ||\n !isRecord(value.bundleManifests) ||\n Object.values(value.bundleManifests).some((manifest) => typeof manifest !== 'string')\n ) {\n throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);\n }\n precompile = {\n version: value.version,\n strategy: value.strategy,\n bootstrapEntry: value.bootstrapEntry,\n bootstrapManifest: value.bootstrapManifest,\n bundleManifests: value.bundleManifests as Record<string, string>,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: stringRecord(state.bundles),\n precompile,\n };\n}\n\nfunction readCompilerManifest(manifestPath: string, generatedDirectory: string): CompilerManifest | undefined {\n if (!isInside(generatedDirectory, manifestPath)) return undefined;\n try {\n const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8')) as unknown;\n if (\n !isRecord(parsed) ||\n typeof parsed.output !== 'string' ||\n !Array.isArray(parsed.artifacts) ||\n parsed.artifacts.some((artifact) => typeof artifact !== 'string') ||\n !Array.isArray(parsed.entries) ||\n parsed.entries.some((entry) => typeof entry !== 'string') ||\n !Array.isArray(parsed.inputs)\n ) {\n return undefined;\n }\n const inputs: InputFingerprint[] = [];\n for (const input of parsed.inputs) {\n // A manifest written before digests were recorded cannot be confirmed by\n // content, so it reads as absent and the next sync rewrites it.\n const fingerprint = parseInputFingerprint(input);\n if (fingerprint === undefined) return undefined;\n inputs.push(fingerprint);\n }\n const artifacts = parsed.artifacts as string[];\n const entries = parsed.entries as string[];\n if (\n !isInside(generatedDirectory, parsed.output) ||\n artifacts.some((artifact) => !isInside(generatedDirectory, artifact))\n ) {\n return undefined;\n }\n return { output: parsed.output, artifacts, entries, inputs };\n } catch {\n return undefined;\n }\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest): boolean {\n if (!fs.existsSync(manifest.output) || manifest.artifacts.some((artifact) => !fs.existsSync(artifact))) return false;\n return inputsAreFresh(manifest.inputs);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n if (!state.bootstrap || !state.precompile) return undefined;\n if (\n state.precompile.version !== PRECOMPILE_STATE_VERSION ||\n canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);\n if (\n !record ||\n !compilerManifestIsFresh(record) ||\n record.output !== state.bootstrap ||\n record.entries.length !== 1 ||\n canonicalPath(record.entries[0] ?? '') !== canonicalPath(expectedBootstrapEntry)\n ) {\n return undefined;\n }\n return record;\n}\n\n/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */\nexport function readStartupBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state) return { bootstrap: undefined, fresh: false };\n return {\n bootstrap: state.bootstrap,\n fresh: freshBootstrapRecord(state, expected) !== undefined,\n };\n}\n\n/** Validates one selected composition without touching any inactive bundle inputs. */\nexport function readBundleStatus(\n repoRoot: string,\n compositionFingerprint: string,\n homeDirectory: string = os.homedir(),\n): BundleStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const bundle = state?.bundles[compositionFingerprint];\n const manifestPath = state?.precompile?.bundleManifests[compositionFingerprint];\n if (!state || state.precompile?.version !== PRECOMPILE_STATE_VERSION || !bundle || !manifestPath) {\n return { bundle, fresh: false };\n }\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return {\n bundle,\n fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest)),\n };\n}\n\n/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */\nexport function readBootstrapStatus(\n repoRoot: string,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapState(repoRoot, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\n if (!state?.bootstrap || !state.precompile || !freshBootstrapRecord(state, expected)) {\n return { bootstrap: state?.bootstrap, fresh: false };\n }\n const manifests = Object.values(state.precompile.bundleManifests);\n if (new Set(manifests).size !== manifests.length) return { bootstrap: state.bootstrap, fresh: false };\n const fresh = Object.entries(state.bundles).every(([fingerprint, bundle]) => {\n const manifestPath = state.precompile?.bundleManifests[fingerprint];\n if (!manifestPath) return false;\n const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);\n return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));\n });\n return { bootstrap: state.bootstrap, fresh };\n}\n\n/** Reads only the generated bootstrap pointer while retaining state validation. */\nexport function readBootstrapPointer(repoRoot: string, homeDirectory: string = os.homedir()): string | undefined {\n return readBootstrapState(repoRoot, homeDirectory)?.bootstrap;\n}\n"],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./modules/moduleResolution.cjs"),n=require("../services/extensionAssembler.cjs"),r=require("./piSettings.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/majorModes"),s=require("node:child_process"),c=require("node:util"),l=require("@earendil-works/pi-coding-agent");const u=`npm:`,d=i.default.join(`.pi`,`npm`),f=i.default.join(d,`node_modules`),p=`package.json`,m=[`const { spawn } = require('node:child_process');`,`const [command, ...args] = process.argv.slice(1);`,`if (!command) throw new Error('Missing package-manager command');`,`const child = spawn(command, args, { stdio: ['ignore', 2, 2] });`,`child.once('error', (error) => { console.error(error.message); process.exit(1); });`,`child.once('exit', (code) => process.exit(code ?? 1));`].join(``),h=(0,c.promisify)(s.execFile);function g(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const _={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},v=new Set(Object.keys(_).map(g));function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e){return y(e)?{...e}:{}}function x(e){let t=b(e);for(let[e,n]of Object.entries(t))v.has(g(e))?delete t[e]:y(n)&&(t[e]=x(n));return t}function S(e){return i.default.join(e,d,p)}function C(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!(0,o.isLocalPackageSpecifier)(e)&&n.add(e)}return[...n]}function w(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function T(e,t,n=new Map){let r=S(e),o=a.default.existsSync(r)?a.default.readFileSync(r,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!y(e))throw Error(`Pi managed package manifest at ${r} must be a JSON object.`);s=e}let c=b(s.dependencies);for(let e of t){let t=D(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...x(s.overrides),..._};let l=b(s.pnpm);l.overrides={...x(l.overrides),..._},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(a.default.mkdirSync(i.default.dirname(r),{recursive:!0}),a.default.writeFileSync(r,u,`utf8`),!0)}function E(e,t,n){return C(e,t).filter(e=>w(n,e)===void 0)}function D(e){
|
|
2
|
-
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./modules/moduleResolution.cjs"),n=require("../services/extensionAssembler.cjs"),r=require("./piSettings.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("@agimon-ai/doompi-config/majorModes"),s=require("node:child_process"),c=require("node:util"),l=require("@earendil-works/pi-coding-agent");const u=`npm:`,d=i.default.join(`.pi`,`npm`),f=i.default.join(d,`node_modules`),p=`package.json`,m=[`const { spawn } = require('node:child_process');`,`const [command, ...args] = process.argv.slice(1);`,`if (!command) throw new Error('Missing package-manager command');`,`const child = spawn(command, args, { stdio: ['ignore', 2, 2] });`,`child.once('error', (error) => { console.error(error.message); process.exit(1); });`,`child.once('exit', (code) => process.exit(code ?? 1));`].join(``),h=(0,c.promisify)(s.execFile);function g(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const _={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},v=new Set(Object.keys(_).map(g));function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e){return y(e)?{...e}:{}}function x(e){let t=b(e);for(let[e,n]of Object.entries(t))v.has(g(e))?delete t[e]:y(n)&&(t[e]=x(n));return t}function S(e){return i.default.join(e,d,p)}function C(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!(0,o.isLocalPackageSpecifier)(e)&&n.add(e)}return[...n]}function w(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function T(e,t,n=new Map){let r=S(e),o=a.default.existsSync(r)?a.default.readFileSync(r,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!y(e))throw Error(`Pi managed package manifest at ${r} must be a JSON object.`);s=e}let c=b(s.dependencies);for(let e of t){let t=D(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...x(s.overrides),..._};let l=b(s.pnpm);l.overrides={...x(l.overrides),..._},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(a.default.mkdirSync(i.default.dirname(r),{recursive:!0}),a.default.writeFileSync(r,u,`utf8`),!0)}function E(e,t,n){return C(e,t).filter(e=>w(n,e)===void 0)}function D(e){let t=e.startsWith(u)?e.slice(4):e;if(t.startsWith(`@`)){let e=t.indexOf(`/`),n=e===-1?-1:t.indexOf(`@`,e);return n===-1?t:t.slice(0,n)}let n=t.indexOf(`@`);return n===-1?t:t.slice(0,n)}function O(e){let t=e.DOOMPI_PACKAGE_CATALOG?.trim();if(!t)return new Map;let n=JSON.parse(a.default.readFileSync(t,`utf8`));if(!y(n)||n.version!==1||!y(n.packages))throw Error(`Unsupported DoomPi package catalog: ${t}`);let r=new Map;for(let[e,o]of Object.entries(n.packages)){if(!y(o)||typeof o.archive!=`string`||!Array.isArray(o.dependencies))throw Error(`Invalid package entry for ${e} in ${t}`);let n=o.dependencies.filter(e=>typeof e==`string`);if(n.length!==o.dependencies.length)throw Error(`Invalid dependencies for ${e} in ${t}`);let s=i.default.resolve(i.default.dirname(t),o.archive);if(!a.default.existsSync(s))throw Error(`DoomPi package catalog archive is missing: ${s}`);r.set(e,{target:`file:${s}`,dependencies:n})}return r}function k(e,n=new Map,r=!1){let i=[...new Set(e.map(e=>t.splitPackageSpecifier(e).name))];if(r)for(let e=0;e<i.length;e+=1)for(let t of n.get(i[e])?.dependencies??[])i.includes(t)||i.push(t);return i.map(e=>{let t=n.get(e)?.target;return`${u}${e}${t?`@${t}`:``}`})}function A(e,t){return`${e} ${t}${e===1?``:`s`}`}function j(e,t){let n=i.default.join(e,f,t,p);if(a.default.existsSync(n))try{let e=JSON.parse(a.default.readFileSync(n,`utf8`));return y(e)&&typeof e.version==`string`?e.version:void 0}catch{return}}function M(e){let t=e?.length?e:[`npm`];return[process.execPath,`--eval`,m,`--`,...t]}function N(e,t){if(e?.length)return e;let n=t.DOOMPI_NPM_CLI?.trim();return n?[process.execPath,n]:void 0}function P(e,t){return N(e.getNpmCommand(),t)}function F(e,t){let n=r.piAgentDirectory(t),i=l.SettingsManager.create(e,n,{projectTrusted:!0}),a=new Proxy(i,{get(e,n,r){return n===`getNpmCommand`?()=>M(P(e,t)):Reflect.get(e,n,r)}});return new l.DefaultPackageManager({cwd:e,agentDir:n,settingsManager:a})}function I(e,t){let[n,...i]=P(l.SettingsManager.create(e,r.piAgentDirectory(t),{projectTrusted:!0}),t)??[],a=n??`npm`;return async t=>{let{stdout:n}=await h(a,[...i,`view`,t,`version`,`--json`],{cwd:e,timeout:2e4,encoding:`utf8`}),r=JSON.parse(n.trim()||`null`);if(typeof r!=`string`)throw Error(`${a} view reported no published version`);return r}}async function L(e,t,n,r){let i=t.map(t=>({name:D(t),from:j(e,D(t))})).filter(e=>e.from!==void 0),a=await Promise.all(i.map(async e=>{try{return{...e,to:await n(e.name)}}catch(t){return{...e,failure:(t instanceof Error?t.message:String(t)).split(`
|
|
2
|
+
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function R(e,t={}){let r=e.environment??process.env,o=t.resolvers??n.createLayerResolvers(e.repoRoot),s=C(e.config,e.layers),c=s.filter(e=>w(o,e)===void 0),l=O(r),u=k(c,l),d=k(s,l,!0);if(d.length===0)return{installed:[],updated:[],unchecked:[]};let f=i.default.dirname(S(e.repoRoot));if(c.length===0&&!a.default.existsSync(f))return{installed:[],updated:[],unchecked:[]};let p=d.filter(e=>!l.has(D(e))),{updated:m,unchecked:h}=e.refresh?await L(e.repoRoot,p,t.publishedVersion??I(e.repoRoot,r),e.onProgress):{updated:[],unchecked:[]},g=new Map;for(let e of d){let t=D(e),n=l.get(t)?.target;n&&g.set(t,n)}for(let e of m)g.set(e.name,e.to);let _=T(e.repoRoot,d,g);if(c.length===0&&!_)return{installed:[],updated:[],unchecked:h};let v=t.packageManager??F(e.repoRoot,r);e.onProgress?.(`installing ${m.length>0?A(m.length,`update`):c.length>0?A(c.length,`missing package`):`the package manifest`}`);try{_?await v.install(d[0],{local:!0}):await v.resolveExtensionSources(u,{local:!0})}catch(e){throw Error(`Failed to reconcile DoomPi layer package(s): ${(_?d:u).join(`, `)}`,{cause:e})}let y=c.filter(e=>!w(o,e)?.length);if(y.length>0)throw Error(`Installed DoomPi layer package(s) did not expose the configured Pi extension: ${y.join(`, `)}`);return{installed:u,updated:m,unchecked:h}}exports.SAFE_TRANSITIVE_OVERRIDES=_,exports.effectivePackageManagerCommand=N,exports.ensureLayerPackages=R,exports.ensureManagedPackageManifest=T,exports.missingLayerPackageSpecifiers=E,exports.packageManagerCommandWithStderr=M;
|
|
3
3
|
//# sourceMappingURL=layerPackageInstaller.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.cjs","names":["path","promisify","execFile","isLocalPackageSpecifier","fs","splitPackageSpecifier","piAgentDirectory","SettingsManager","DefaultPackageManager","createLayerResolvers"],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst REGISTRY_TIMEOUT_MS = 20_000;\nconst PACKAGE_MANAGER_STDERR_SCRIPT = [\n \"const { spawn } = require('node:child_process');\",\n 'const [command, ...args] = process.argv.slice(1);',\n \"if (!command) throw new Error('Missing package-manager command');\",\n \"const child = spawn(command, args, { stdio: ['ignore', 2, 2] });\",\n \"child.once('error', (error) => { console.error(error.message); process.exit(1); });\",\n \"child.once('exit', (code) => process.exit(code ?? 1));\",\n].join('');\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n return source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nexport function packageManagerCommandWithStderr(configured: readonly string[] | undefined): string[] {\n const command = configured?.length ? configured : [DEFAULT_NPM_COMMAND];\n return [process.execPath, '--eval', PACKAGE_MANAGER_STDERR_SCRIPT, '--', ...command];\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n const redirectedSettingsManager = new Proxy(settingsManager, {\n get(target, property, receiver) {\n if (property === 'getNpmCommand') return () => packageManagerCommandWithStderr(target.getNpmCommand());\n return Reflect.get(target, property, receiver) as unknown;\n },\n });\n return new DefaultPackageManager({\n cwd: repoRoot,\n agentDir: agentDirectory,\n settingsManager: redirectedSettingsManager,\n });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const configured = SettingsManager.create(repoRoot, piAgentDirectory(environment), {\n projectTrusted: true,\n }).getNpmCommand();\n const [command, ...args] = configured ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n const clientArguments = command ? args : [];\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...clientArguments, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n sources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const manifestChanged = ensureManagedPackageManifest(\n options.repoRoot,\n sources,\n new Map(updated.map((update) => [update.name, update.to])),\n );\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.cjs","names":["path","promisify","execFile","isLocalPackageSpecifier","fs","splitPackageSpecifier","piAgentDirectory","SettingsManager","DefaultPackageManager","createLayerResolvers"],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst NPM_CLI_ENV = 'DOOMPI_NPM_CLI';\nconst PACKAGE_CATALOG_ENV = 'DOOMPI_PACKAGE_CATALOG';\nconst REGISTRY_TIMEOUT_MS = 20_000;\nconst PACKAGE_MANAGER_STDERR_SCRIPT = [\n \"const { spawn } = require('node:child_process');\",\n 'const [command, ...args] = process.argv.slice(1);',\n \"if (!command) throw new Error('Missing package-manager command');\",\n \"const child = spawn(command, args, { stdio: ['ignore', 2, 2] });\",\n \"child.once('error', (error) => { console.error(error.message); process.exit(1); });\",\n \"child.once('exit', (code) => process.exit(code ?? 1));\",\n].join('');\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n const specifier = source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n if (specifier.startsWith('@')) {\n const slash = specifier.indexOf('/');\n const version = slash === -1 ? -1 : specifier.indexOf('@', slash);\n return version === -1 ? specifier : specifier.slice(0, version);\n }\n const version = specifier.indexOf('@');\n return version === -1 ? specifier : specifier.slice(0, version);\n}\n\ninterface PackageCatalogEntry {\n target: string;\n dependencies: readonly string[];\n}\n\ntype PackageCatalog = ReadonlyMap<string, PackageCatalogEntry>;\n\nfunction readPackageCatalog(environment: NodeJS.ProcessEnv): PackageCatalog {\n const manifestPath = environment[PACKAGE_CATALOG_ENV]?.trim();\n if (!manifestPath) return new Map();\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n if (!isRecord(parsed) || parsed.version !== 1 || !isRecord(parsed.packages)) {\n throw new Error(`Unsupported DoomPi package catalog: ${manifestPath}`);\n }\n const catalog = new Map<string, PackageCatalogEntry>();\n for (const [name, value] of Object.entries(parsed.packages)) {\n if (!isRecord(value) || typeof value.archive !== 'string' || !Array.isArray(value.dependencies)) {\n throw new Error(`Invalid package entry for ${name} in ${manifestPath}`);\n }\n const dependencies = value.dependencies.filter(\n (dependency): dependency is string => typeof dependency === 'string',\n );\n if (dependencies.length !== value.dependencies.length)\n throw new Error(`Invalid dependencies for ${name} in ${manifestPath}`);\n const archive = path.resolve(path.dirname(manifestPath), value.archive);\n if (!fs.existsSync(archive)) throw new Error(`DoomPi package catalog archive is missing: ${archive}`);\n catalog.set(name, { target: `file:${archive}`, dependencies });\n }\n return catalog;\n}\n\nfunction npmSources(\n specifiers: readonly string[],\n catalog: PackageCatalog = new Map(),\n includeCatalogDependencies = false,\n): string[] {\n const names = [...new Set(specifiers.map((specifier) => splitPackageSpecifier(specifier).name))];\n if (includeCatalogDependencies) {\n for (let index = 0; index < names.length; index += 1) {\n for (const dependency of catalog.get(names[index]!)?.dependencies ?? []) {\n if (!names.includes(dependency)) names.push(dependency);\n }\n }\n }\n return names.map((name) => {\n const target = catalog.get(name)?.target;\n return `${NPM_SOURCE_PREFIX}${name}${target ? `@${target}` : ''}`;\n });\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nexport function packageManagerCommandWithStderr(configured: readonly string[] | undefined): string[] {\n const command = configured?.length ? configured : [DEFAULT_NPM_COMMAND];\n return [process.execPath, '--eval', PACKAGE_MANAGER_STDERR_SCRIPT, '--', ...command];\n}\n\nexport function effectivePackageManagerCommand(\n configured: readonly string[] | undefined,\n environment: NodeJS.ProcessEnv,\n): readonly string[] | undefined {\n if (configured?.length) return configured;\n const bundledCli = environment[NPM_CLI_ENV]?.trim();\n return bundledCli ? [process.execPath, bundledCli] : undefined;\n}\n\nfunction configuredPackageManagerCommand(\n settingsManager: Pick<SettingsManager, 'getNpmCommand'>,\n environment: NodeJS.ProcessEnv,\n): readonly string[] | undefined {\n return effectivePackageManagerCommand(settingsManager.getNpmCommand(), environment);\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n const redirectedSettingsManager = new Proxy(settingsManager, {\n get(target, property, receiver) {\n if (property === 'getNpmCommand') {\n return () => packageManagerCommandWithStderr(configuredPackageManagerCommand(target, environment));\n }\n return Reflect.get(target, property, receiver) as unknown;\n },\n });\n return new DefaultPackageManager({\n cwd: repoRoot,\n agentDir: agentDirectory,\n settingsManager: redirectedSettingsManager,\n });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const settingsManager = SettingsManager.create(repoRoot, piAgentDirectory(environment), { projectTrusted: true });\n const [command, ...args] = configuredPackageManagerCommand(settingsManager, environment) ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...args, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const catalog = readPackageCatalog(environment);\n const missingSources = npmSources(missing, catalog);\n const sources = npmSources(configured, catalog, true);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const registrySources = sources.filter((source) => !catalog.has(packageName(source)));\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n registrySources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const targets = new Map<string, string>();\n for (const source of sources) {\n const name = packageName(source);\n const target = catalog.get(name)?.target;\n if (target) targets.set(name, target);\n }\n for (const update of updated) targets.set(update.name, update.to);\n const manifestChanged = ensureManagedPackageManifest(options.repoRoot, sources, targets);\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.d.cts","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.d.cts","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"mappings":";;;;KAsDK,sBAAsB,KAAK;UAEf;EACf;EACA,QAAQ;EACR;EACA,cAAc,OAAO;;;;;;;;EAQrB;;EAEA,cAAc;;UAGC;EACf,iBAAiB;EACjB,YAAY;;EAEZ,oBAAoB,iBAAiB;;;UAItB;EACf;EACA;EACA;;UAGe;;EAEf;;EAEA,SAAS;;EAET;;;iBAySoB,oBACpB,SAAS,4BACT,eAAc,oCACb,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.d.mts","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.d.mts","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"mappings":";;;;KAsDK,sBAAsB,KAAK;UAEf;EACf;EACA,QAAQ;EACR;EACA,cAAc,OAAO;;;;;;;;EAQrB;;EAEA,cAAc;;UAGC;EACf,iBAAiB;EACjB,YAAY;;EAEZ,oBAAoB,iBAAiB;;;UAItB;EACf;EACA;EACA;;UAGe;;EAEf;;EAEA,SAAS;;EAET;;;iBAySoB,oBACpB,SAAS,4BACT,eAAc,oCACb,QAAQ"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import{splitPackageSpecifier as e}from"./modules/moduleResolution.mjs";import{createLayerResolvers as t}from"../services/extensionAssembler.mjs";import{piAgentDirectory as n}from"./piSettings.mjs";import r from"node:path";import i from"node:fs";import{isLocalPackageSpecifier as a}from"@agimon-ai/doompi-config/majorModes";import{execFile as o}from"node:child_process";import{promisify as s}from"node:util";import{DefaultPackageManager as c,SettingsManager as l}from"@earendil-works/pi-coding-agent";const u=`npm:`,d=r.join(`.pi`,`npm`),f=r.join(d,`node_modules`),p=`package.json`,m=[`const { spawn } = require('node:child_process');`,`const [command, ...args] = process.argv.slice(1);`,`if (!command) throw new Error('Missing package-manager command');`,`const child = spawn(command, args, { stdio: ['ignore', 2, 2] });`,`child.once('error', (error) => { console.error(error.message); process.exit(1); });`,`child.once('exit', (code) => process.exit(code ?? 1));`].join(``),h=s(o);function g(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const _={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},v=new Set(Object.keys(_).map(g));function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e){return y(e)?{...e}:{}}function x(e){let t=b(e);for(let[e,n]of Object.entries(t))v.has(g(e))?delete t[e]:y(n)&&(t[e]=x(n));return t}function S(e){return r.join(e,d,p)}function C(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!a(e)&&n.add(e)}return[...n]}function w(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function T(e,t,n=new Map){let a=S(e),o=i.existsSync(a)?i.readFileSync(a,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!y(e))throw Error(`Pi managed package manifest at ${a} must be a JSON object.`);s=e}let c=b(s.dependencies);for(let e of t){let t=D(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...x(s.overrides),..._};let l=b(s.pnpm);l.overrides={...x(l.overrides),..._},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(i.mkdirSync(r.dirname(a),{recursive:!0}),i.writeFileSync(a,u,`utf8`),!0)}function E(e,t,n){return C(e,t).filter(e=>w(n,e)===void 0)}function D(e){
|
|
2
|
-
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function
|
|
1
|
+
import{splitPackageSpecifier as e}from"./modules/moduleResolution.mjs";import{createLayerResolvers as t}from"../services/extensionAssembler.mjs";import{piAgentDirectory as n}from"./piSettings.mjs";import r from"node:path";import i from"node:fs";import{isLocalPackageSpecifier as a}from"@agimon-ai/doompi-config/majorModes";import{execFile as o}from"node:child_process";import{promisify as s}from"node:util";import{DefaultPackageManager as c,SettingsManager as l}from"@earendil-works/pi-coding-agent";const u=`npm:`,d=r.join(`.pi`,`npm`),f=r.join(d,`node_modules`),p=`package.json`,m=[`const { spawn } = require('node:child_process');`,`const [command, ...args] = process.argv.slice(1);`,`if (!command) throw new Error('Missing package-manager command');`,`const child = spawn(command, args, { stdio: ['ignore', 2, 2] });`,`child.once('error', (error) => { console.error(error.message); process.exit(1); });`,`child.once('exit', (code) => process.exit(code ?? 1));`].join(``),h=s(o);function g(e){let t=e.indexOf(`@`,+!!e.startsWith(`@`));return t===-1?e:e.slice(0,t)}const _={"@hono/node-server@2.0.0 - 2.0.9":`^2.0.10`,"@opentelemetry/core@2.0.0 - 2.7.999":`2.8.0`,"brace-expansion@5.0.0 - 5.0.8":`^5.0.9`,"hono@4.0.0 - 4.12.33":`^4.12.34`,"js-yaml@4.0.0 - 4.3.0":`^4.3.1`,"js-yaml@5.0.0 - 5.2.1":`^5.2.2`,"liquidjs@10.0.0 - 10.27.0":`^10.27.1`,"protobufjs@8.0.0 - 8.7.1":`^8.7.2`},v=new Set(Object.keys(_).map(g));function y(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function b(e){return y(e)?{...e}:{}}function x(e){let t=b(e);for(let[e,n]of Object.entries(t))v.has(g(e))?delete t[e]:y(n)&&(t[e]=x(n));return t}function S(e){return r.join(e,d,p)}function C(e,t){let n=new Set,r=[...e.default?[e.default]:[],...t.map(t=>{let n=e.layers[t];if(!n)throw Error(`Unknown layer: ${t}`);return n})];for(let e of r)for(let t of e.packages??[]){let e=typeof t==`string`?t:t.name;(typeof t==`string`||t.optional!==!0)&&!a(e)&&n.add(e)}return[...n]}function w(e,t){let n=e.optionalPackageEntries?.(t);if(n!==void 0)return n;let r=e.optionalPackageEntry(t);return r?[r]:void 0}function T(e,t,n=new Map){let a=S(e),o=i.existsSync(a)?i.readFileSync(a,`utf8`):void 0,s;if(o===void 0)s={name:`pi-extensions`,private:!0};else{let e=JSON.parse(o);if(!y(e))throw Error(`Pi managed package manifest at ${a} must be a JSON object.`);s=e}let c=b(s.dependencies);for(let e of t){let t=D(e),r=n.get(t);r===void 0?Object.hasOwn(c,t)||(c[t]=`*`):c[t]=r}s.dependencies=c,s.overrides={...x(s.overrides),..._};let l=b(s.pnpm);l.overrides={...x(l.overrides),..._},s.pnpm=l;let u=`${JSON.stringify(s,null,2)}\n`;return u!==o&&(i.mkdirSync(r.dirname(a),{recursive:!0}),i.writeFileSync(a,u,`utf8`),!0)}function E(e,t,n){return C(e,t).filter(e=>w(n,e)===void 0)}function D(e){let t=e.startsWith(u)?e.slice(4):e;if(t.startsWith(`@`)){let e=t.indexOf(`/`),n=e===-1?-1:t.indexOf(`@`,e);return n===-1?t:t.slice(0,n)}let n=t.indexOf(`@`);return n===-1?t:t.slice(0,n)}function O(e){let t=e.DOOMPI_PACKAGE_CATALOG?.trim();if(!t)return new Map;let n=JSON.parse(i.readFileSync(t,`utf8`));if(!y(n)||n.version!==1||!y(n.packages))throw Error(`Unsupported DoomPi package catalog: ${t}`);let a=new Map;for(let[e,o]of Object.entries(n.packages)){if(!y(o)||typeof o.archive!=`string`||!Array.isArray(o.dependencies))throw Error(`Invalid package entry for ${e} in ${t}`);let n=o.dependencies.filter(e=>typeof e==`string`);if(n.length!==o.dependencies.length)throw Error(`Invalid dependencies for ${e} in ${t}`);let s=r.resolve(r.dirname(t),o.archive);if(!i.existsSync(s))throw Error(`DoomPi package catalog archive is missing: ${s}`);a.set(e,{target:`file:${s}`,dependencies:n})}return a}function k(t,n=new Map,r=!1){let i=[...new Set(t.map(t=>e(t).name))];if(r)for(let e=0;e<i.length;e+=1)for(let t of n.get(i[e])?.dependencies??[])i.includes(t)||i.push(t);return i.map(e=>{let t=n.get(e)?.target;return`${u}${e}${t?`@${t}`:``}`})}function A(e,t){return`${e} ${t}${e===1?``:`s`}`}function j(e,t){let n=r.join(e,f,t,p);if(i.existsSync(n))try{let e=JSON.parse(i.readFileSync(n,`utf8`));return y(e)&&typeof e.version==`string`?e.version:void 0}catch{return}}function M(e){let t=e?.length?e:[`npm`];return[process.execPath,`--eval`,m,`--`,...t]}function N(e,t){if(e?.length)return e;let n=t.DOOMPI_NPM_CLI?.trim();return n?[process.execPath,n]:void 0}function P(e,t){return N(e.getNpmCommand(),t)}function F(e,t){let r=n(t),i=l.create(e,r,{projectTrusted:!0}),a=new Proxy(i,{get(e,n,r){return n===`getNpmCommand`?()=>M(P(e,t)):Reflect.get(e,n,r)}});return new c({cwd:e,agentDir:r,settingsManager:a})}function I(e,t){let[r,...i]=P(l.create(e,n(t),{projectTrusted:!0}),t)??[],a=r??`npm`;return async t=>{let{stdout:n}=await h(a,[...i,`view`,t,`version`,`--json`],{cwd:e,timeout:2e4,encoding:`utf8`}),r=JSON.parse(n.trim()||`null`);if(typeof r!=`string`)throw Error(`${a} view reported no published version`);return r}}async function L(e,t,n,r){let i=t.map(t=>({name:D(t),from:j(e,D(t))})).filter(e=>e.from!==void 0),a=await Promise.all(i.map(async e=>{try{return{...e,to:await n(e.name)}}catch(t){return{...e,failure:(t instanceof Error?t.message:String(t)).split(`
|
|
2
|
+
`)[0]}}})),o=[],s=[];for(let e of a){if(e.to===void 0){s.push(e.name),r?.(`kept ${e.name} ${e.from}: ${e.failure??`no published version`}`);continue}e.to!==e.from&&(o.push({name:e.name,from:e.from,to:e.to}),r?.(`${e.name} ${e.from} -> ${e.to}`))}return{updated:o,unchecked:s}}async function R(e,n={}){let a=e.environment??process.env,o=n.resolvers??t(e.repoRoot),s=C(e.config,e.layers),c=s.filter(e=>w(o,e)===void 0),l=O(a),u=k(c,l),d=k(s,l,!0);if(d.length===0)return{installed:[],updated:[],unchecked:[]};let f=r.dirname(S(e.repoRoot));if(c.length===0&&!i.existsSync(f))return{installed:[],updated:[],unchecked:[]};let p=d.filter(e=>!l.has(D(e))),{updated:m,unchecked:h}=e.refresh?await L(e.repoRoot,p,n.publishedVersion??I(e.repoRoot,a),e.onProgress):{updated:[],unchecked:[]},g=new Map;for(let e of d){let t=D(e),n=l.get(t)?.target;n&&g.set(t,n)}for(let e of m)g.set(e.name,e.to);let _=T(e.repoRoot,d,g);if(c.length===0&&!_)return{installed:[],updated:[],unchecked:h};let v=n.packageManager??F(e.repoRoot,a);e.onProgress?.(`installing ${m.length>0?A(m.length,`update`):c.length>0?A(c.length,`missing package`):`the package manifest`}`);try{_?await v.install(d[0],{local:!0}):await v.resolveExtensionSources(u,{local:!0})}catch(e){throw Error(`Failed to reconcile DoomPi layer package(s): ${(_?d:u).join(`, `)}`,{cause:e})}let y=c.filter(e=>!w(o,e)?.length);if(y.length>0)throw Error(`Installed DoomPi layer package(s) did not expose the configured Pi extension: ${y.join(`, `)}`);return{installed:u,updated:m,unchecked:h}}export{_ as SAFE_TRANSITIVE_OVERRIDES,N as effectivePackageManagerCommand,R as ensureLayerPackages,T as ensureManagedPackageManifest,E as missingLayerPackageSpecifiers,M as packageManagerCommandWithStderr};
|
|
3
3
|
//# sourceMappingURL=layerPackageInstaller.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layerPackageInstaller.mjs","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst REGISTRY_TIMEOUT_MS = 20_000;\nconst PACKAGE_MANAGER_STDERR_SCRIPT = [\n \"const { spawn } = require('node:child_process');\",\n 'const [command, ...args] = process.argv.slice(1);',\n \"if (!command) throw new Error('Missing package-manager command');\",\n \"const child = spawn(command, args, { stdio: ['ignore', 2, 2] });\",\n \"child.once('error', (error) => { console.error(error.message); process.exit(1); });\",\n \"child.once('exit', (code) => process.exit(code ?? 1));\",\n].join('');\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n return source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n}\n\nfunction npmSources(specifiers: readonly string[]): string[] {\n return [...new Set(specifiers.map((specifier) => `${NPM_SOURCE_PREFIX}${splitPackageSpecifier(specifier).name}`))];\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nexport function packageManagerCommandWithStderr(configured: readonly string[] | undefined): string[] {\n const command = configured?.length ? configured : [DEFAULT_NPM_COMMAND];\n return [process.execPath, '--eval', PACKAGE_MANAGER_STDERR_SCRIPT, '--', ...command];\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n const redirectedSettingsManager = new Proxy(settingsManager, {\n get(target, property, receiver) {\n if (property === 'getNpmCommand') return () => packageManagerCommandWithStderr(target.getNpmCommand());\n return Reflect.get(target, property, receiver) as unknown;\n },\n });\n return new DefaultPackageManager({\n cwd: repoRoot,\n agentDir: agentDirectory,\n settingsManager: redirectedSettingsManager,\n });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const configured = SettingsManager.create(repoRoot, piAgentDirectory(environment), {\n projectTrusted: true,\n }).getNpmCommand();\n const [command, ...args] = configured ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n const clientArguments = command ? args : [];\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...clientArguments, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const missingSources = npmSources(missing);\n const sources = npmSources(configured);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n sources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const manifestChanged = ensureManagedPackageManifest(\n options.repoRoot,\n sources,\n new Map(updated.map((update) => [update.name, update.to])),\n );\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"layerPackageInstaller.mjs","names":[],"sources":["../../../src/adapters/layerPackageInstaller.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { promisify } from 'node:util';\nimport { isLocalPackageSpecifier, type MajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { DefaultPackageManager, type PackageManager, SettingsManager } from '@earendil-works/pi-coding-agent';\nimport { createLayerResolvers, type ExtensionLayerResolvers } from '../services/extensionAssembler.ts';\nimport { splitPackageSpecifier } from './modules/moduleResolution.ts';\nimport { piAgentDirectory } from './piSettings.ts';\n\nconst NPM_SOURCE_PREFIX = 'npm:';\nconst PI_MANAGED_NPM_DIRECTORY = path.join('.pi', 'npm');\nconst PI_MANAGED_MODULES_DIRECTORY = path.join(PI_MANAGED_NPM_DIRECTORY, 'node_modules');\nconst PI_MANAGED_PACKAGE_NAME = 'pi-extensions';\nconst UNPINNED_PACKAGE_RANGE = '*';\nconst PACKAGE_MANIFEST = 'package.json';\nconst DEFAULT_NPM_COMMAND = 'npm';\nconst NPM_CLI_ENV = 'DOOMPI_NPM_CLI';\nconst PACKAGE_CATALOG_ENV = 'DOOMPI_PACKAGE_CATALOG';\nconst REGISTRY_TIMEOUT_MS = 20_000;\nconst PACKAGE_MANAGER_STDERR_SCRIPT = [\n \"const { spawn } = require('node:child_process');\",\n 'const [command, ...args] = process.argv.slice(1);',\n \"if (!command) throw new Error('Missing package-manager command');\",\n \"const child = spawn(command, args, { stdio: ['ignore', 2, 2] });\",\n \"child.once('error', (error) => { console.error(error.message); process.exit(1); });\",\n \"child.once('exit', (code) => process.exit(code ?? 1));\",\n].join('');\n\nconst execFileAsync = promisify(execFile);\n\nfunction overridePackageName(selector: string): string {\n const separatorIndex = selector.indexOf('@', selector.startsWith('@') ? 1 : 0);\n return separatorIndex === -1 ? selector : selector.slice(0, separatorIndex);\n}\n\n/**\n * Same-major patches for transitive versions pinned by the current foundation packages.\n * npm only honors overrides from the install root, so published extensions cannot carry\n * these fixes in their own manifests.\n */\nexport const SAFE_TRANSITIVE_OVERRIDES = {\n '@hono/node-server@2.0.0 - 2.0.9': '^2.0.10',\n '@opentelemetry/core@2.0.0 - 2.7.999': '2.8.0',\n 'brace-expansion@5.0.0 - 5.0.8': '^5.0.9',\n 'hono@4.0.0 - 4.12.33': '^4.12.34',\n 'js-yaml@4.0.0 - 4.3.0': '^4.3.1',\n 'js-yaml@5.0.0 - 5.2.1': '^5.2.2',\n 'liquidjs@10.0.0 - 10.27.0': '^10.27.1',\n 'protobufjs@8.0.0 - 8.7.1': '^8.7.2',\n} as const satisfies Readonly<Record<string, string>>;\n\nconst SAFE_TRANSITIVE_PACKAGE_NAMES = new Set(Object.keys(SAFE_TRANSITIVE_OVERRIDES).map(overridePackageName));\n\ntype LayerPackageManager = Pick<PackageManager, 'install' | 'resolveExtensionSources'>;\n\nexport interface EnsureLayerPackagesOptions {\n repoRoot: string;\n config: MajorModesConfig;\n layers: readonly string[];\n environment?: NodeJS.ProcessEnv;\n /**\n * Move every managed package to its newest published version.\n *\n * Launch installs what a selection needs and nothing more, so a session never\n * pays for a registry round trip. Sync is the explicit maintenance command and\n * is the only caller that asks for this.\n */\n refresh?: boolean;\n /** Receives one human-readable line per package decision. */\n onProgress?: (message: string) => void;\n}\n\nexport interface LayerPackageInstallerDependencies {\n packageManager?: LayerPackageManager;\n resolvers?: ExtensionLayerResolvers;\n /** Registry lookup seam; the default reads the newest version with Pi's npm client. */\n publishedVersion?: (name: string) => Promise<string>;\n}\n\n/** One managed package moving from the installed version to the newest published one. */\nexport interface LayerPackageUpdate {\n name: string;\n from: string;\n to: string;\n}\n\nexport interface LayerPackageResult {\n /** Sources installed because a configured extension could not be resolved. */\n installed: string[];\n /** Packages this run moved to a newer published version. */\n updated: LayerPackageUpdate[];\n /** Packages left at the installed version because the registry could not be read. */\n unchecked: string[];\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction recordValue(value: unknown): Record<string, unknown> {\n return isRecord(value) ? { ...value } : {};\n}\n\nfunction withoutConflictingTransitiveOverrides(value: unknown): Record<string, unknown> {\n const overrides = recordValue(value);\n for (const [selector, replacement] of Object.entries(overrides)) {\n if (SAFE_TRANSITIVE_PACKAGE_NAMES.has(overridePackageName(selector))) {\n delete overrides[selector];\n } else if (isRecord(replacement)) {\n overrides[selector] = withoutConflictingTransitiveOverrides(replacement);\n }\n }\n return overrides;\n}\n\nfunction managedPackageManifestPath(repoRoot: string): string {\n return path.join(repoRoot, PI_MANAGED_NPM_DIRECTORY, PACKAGE_MANIFEST);\n}\n\nfunction configuredPackageSpecifiers(config: MajorModesConfig, layers: readonly string[]): string[] {\n const specifiers = new Set<string>();\n const selectedDefinitions = [\n ...(config.default ? [config.default] : []),\n ...layers.map((layerName) => {\n const layer = config.layers[layerName];\n if (!layer) throw new Error(`Unknown layer: ${layerName}`);\n return layer;\n }),\n ];\n for (const layer of selectedDefinitions) {\n for (const configured of layer.packages ?? []) {\n const specifier = typeof configured === 'string' ? configured : configured.name;\n const optional = typeof configured !== 'string' && configured.optional === true;\n if (!optional && !isLocalPackageSpecifier(specifier)) specifiers.add(specifier);\n }\n }\n return [...specifiers];\n}\n\nfunction resolvePackageEntries(resolvers: ExtensionLayerResolvers, specifier: string): string[] | undefined {\n const entries = resolvers.optionalPackageEntries?.(specifier);\n if (entries !== undefined) return entries;\n const entry = resolvers.optionalPackageEntry(specifier);\n return entry ? [entry] : undefined;\n}\n\n/** Adds the complete package set and patched transitive policy to Pi's managed install root. */\nexport function ensureManagedPackageManifest(\n repoRoot: string,\n sources: readonly string[],\n targets: ReadonlyMap<string, string> = new Map(),\n): boolean {\n const manifestPath = managedPackageManifestPath(repoRoot);\n const current = fs.existsSync(manifestPath) ? fs.readFileSync(manifestPath, 'utf8') : undefined;\n let manifest: Record<string, unknown>;\n if (current === undefined) {\n manifest = { name: PI_MANAGED_PACKAGE_NAME, private: true };\n } else {\n const parsed: unknown = JSON.parse(current);\n if (!isRecord(parsed)) throw new Error(`Pi managed package manifest at ${manifestPath} must be a JSON object.`);\n manifest = parsed;\n }\n\n const dependencies = recordValue(manifest.dependencies);\n for (const source of sources) {\n const name = packageName(source);\n const target = targets.get(name);\n // An exact target is what makes an update reach the store: npm keeps a\n // locked version whenever the recorded range still admits it, and the range\n // it writes back after an install always admits the version it just wrote.\n if (target !== undefined) dependencies[name] = target;\n else if (!Object.hasOwn(dependencies, name)) dependencies[name] = UNPINNED_PACKAGE_RANGE;\n }\n manifest.dependencies = dependencies;\n // npm gives nested and overlapping consumer rules precedence in cases that can retain\n // vulnerable versions, so DoomPi owns these package-specific selectors recursively.\n manifest.overrides = {\n ...withoutConflictingTransitiveOverrides(manifest.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n const pnpm = recordValue(manifest.pnpm);\n pnpm.overrides = {\n ...withoutConflictingTransitiveOverrides(pnpm.overrides),\n ...SAFE_TRANSITIVE_OVERRIDES,\n };\n manifest.pnpm = pnpm;\n\n const serialized = `${JSON.stringify(manifest, null, 2)}\\n`;\n if (serialized === current) return false;\n fs.mkdirSync(path.dirname(manifestPath), { recursive: true });\n fs.writeFileSync(manifestPath, serialized, 'utf8');\n return true;\n}\n\n/** Finds required npm packages selected by defaults and active layers that DoomPi cannot resolve. */\nexport function missingLayerPackageSpecifiers(\n config: MajorModesConfig,\n layers: readonly string[],\n resolvers: ExtensionLayerResolvers,\n): string[] {\n return configuredPackageSpecifiers(config, layers).filter(\n (specifier) => resolvePackageEntries(resolvers, specifier) === undefined,\n );\n}\n\nfunction packageName(source: string): string {\n const specifier = source.startsWith(NPM_SOURCE_PREFIX) ? source.slice(NPM_SOURCE_PREFIX.length) : source;\n if (specifier.startsWith('@')) {\n const slash = specifier.indexOf('/');\n const version = slash === -1 ? -1 : specifier.indexOf('@', slash);\n return version === -1 ? specifier : specifier.slice(0, version);\n }\n const version = specifier.indexOf('@');\n return version === -1 ? specifier : specifier.slice(0, version);\n}\n\ninterface PackageCatalogEntry {\n target: string;\n dependencies: readonly string[];\n}\n\ntype PackageCatalog = ReadonlyMap<string, PackageCatalogEntry>;\n\nfunction readPackageCatalog(environment: NodeJS.ProcessEnv): PackageCatalog {\n const manifestPath = environment[PACKAGE_CATALOG_ENV]?.trim();\n if (!manifestPath) return new Map();\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n if (!isRecord(parsed) || parsed.version !== 1 || !isRecord(parsed.packages)) {\n throw new Error(`Unsupported DoomPi package catalog: ${manifestPath}`);\n }\n const catalog = new Map<string, PackageCatalogEntry>();\n for (const [name, value] of Object.entries(parsed.packages)) {\n if (!isRecord(value) || typeof value.archive !== 'string' || !Array.isArray(value.dependencies)) {\n throw new Error(`Invalid package entry for ${name} in ${manifestPath}`);\n }\n const dependencies = value.dependencies.filter(\n (dependency): dependency is string => typeof dependency === 'string',\n );\n if (dependencies.length !== value.dependencies.length)\n throw new Error(`Invalid dependencies for ${name} in ${manifestPath}`);\n const archive = path.resolve(path.dirname(manifestPath), value.archive);\n if (!fs.existsSync(archive)) throw new Error(`DoomPi package catalog archive is missing: ${archive}`);\n catalog.set(name, { target: `file:${archive}`, dependencies });\n }\n return catalog;\n}\n\nfunction npmSources(\n specifiers: readonly string[],\n catalog: PackageCatalog = new Map(),\n includeCatalogDependencies = false,\n): string[] {\n const names = [...new Set(specifiers.map((specifier) => splitPackageSpecifier(specifier).name))];\n if (includeCatalogDependencies) {\n for (let index = 0; index < names.length; index += 1) {\n for (const dependency of catalog.get(names[index]!)?.dependencies ?? []) {\n if (!names.includes(dependency)) names.push(dependency);\n }\n }\n }\n return names.map((name) => {\n const target = catalog.get(name)?.target;\n return `${NPM_SOURCE_PREFIX}${name}${target ? `@${target}` : ''}`;\n });\n}\n\nfunction pluralize(count: number, noun: string): string {\n return `${count} ${noun}${count === 1 ? '' : 's'}`;\n}\n\n/** Version of a package inside Pi's managed store, absent when sync does not own it. */\nfunction managedPackageVersion(repoRoot: string, name: string): string | undefined {\n const manifestPath = path.join(repoRoot, PI_MANAGED_MODULES_DIRECTORY, name, PACKAGE_MANIFEST);\n if (!fs.existsSync(manifestPath)) return undefined;\n try {\n const parsed: unknown = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));\n return isRecord(parsed) && typeof parsed.version === 'string' ? parsed.version : undefined;\n } catch {\n // An unreadable manifest carries the same meaning as an absent one: nothing\n // usable is installed, so the caller treats the package as uninstalled.\n return undefined;\n }\n}\n\nexport function packageManagerCommandWithStderr(configured: readonly string[] | undefined): string[] {\n const command = configured?.length ? configured : [DEFAULT_NPM_COMMAND];\n return [process.execPath, '--eval', PACKAGE_MANAGER_STDERR_SCRIPT, '--', ...command];\n}\n\nexport function effectivePackageManagerCommand(\n configured: readonly string[] | undefined,\n environment: NodeJS.ProcessEnv,\n): readonly string[] | undefined {\n if (configured?.length) return configured;\n const bundledCli = environment[NPM_CLI_ENV]?.trim();\n return bundledCli ? [process.execPath, bundledCli] : undefined;\n}\n\nfunction configuredPackageManagerCommand(\n settingsManager: Pick<SettingsManager, 'getNpmCommand'>,\n environment: NodeJS.ProcessEnv,\n): readonly string[] | undefined {\n return effectivePackageManagerCommand(settingsManager.getNpmCommand(), environment);\n}\n\nfunction createPackageManager(repoRoot: string, environment: NodeJS.ProcessEnv): LayerPackageManager {\n const agentDirectory = piAgentDirectory(environment);\n const settingsManager = SettingsManager.create(repoRoot, agentDirectory, { projectTrusted: true });\n const redirectedSettingsManager = new Proxy(settingsManager, {\n get(target, property, receiver) {\n if (property === 'getNpmCommand') {\n return () => packageManagerCommandWithStderr(configuredPackageManagerCommand(target, environment));\n }\n return Reflect.get(target, property, receiver) as unknown;\n },\n });\n return new DefaultPackageManager({\n cwd: repoRoot,\n agentDir: agentDirectory,\n settingsManager: redirectedSettingsManager,\n });\n}\n\n/** Reads newest published versions with the same client Pi installs them with. */\nfunction createVersionReader(repoRoot: string, environment: NodeJS.ProcessEnv): (name: string) => Promise<string> {\n const settingsManager = SettingsManager.create(repoRoot, piAgentDirectory(environment), { projectTrusted: true });\n const [command, ...args] = configuredPackageManagerCommand(settingsManager, environment) ?? [];\n const client = command ?? DEFAULT_NPM_COMMAND;\n return async (name) => {\n const { stdout } = await execFileAsync(client, [...args, 'view', name, 'version', '--json'], {\n cwd: repoRoot,\n timeout: REGISTRY_TIMEOUT_MS,\n encoding: 'utf8',\n });\n const parsed: unknown = JSON.parse(stdout.trim() || 'null');\n if (typeof parsed !== 'string') throw new Error(`${client} view reported no published version`);\n return parsed;\n };\n}\n\ninterface CheckedPackage {\n name: string;\n from: string;\n to?: string;\n failure?: string;\n}\n\n/**\n * Compares every package sync installed against the registry.\n *\n * Only the managed store is consulted: a package that resolves from the\n * repository's own modules is not sync's to move, and a package that is not\n * installed yet is already covered by the missing-package path.\n */\nasync function resolveUpdates(\n repoRoot: string,\n sources: readonly string[],\n publishedVersion: (name: string) => Promise<string>,\n onProgress: ((message: string) => void) | undefined,\n): Promise<Pick<LayerPackageResult, 'updated' | 'unchecked'>> {\n const managed = sources\n .map((source) => ({ name: packageName(source), from: managedPackageVersion(repoRoot, packageName(source)) }))\n .filter((entry): entry is { name: string; from: string } => entry.from !== undefined);\n const checked: CheckedPackage[] = await Promise.all(\n managed.map(async (entry) => {\n try {\n return { ...entry, to: await publishedVersion(entry.name) };\n } catch (error) {\n return { ...entry, failure: (error instanceof Error ? error.message : String(error)).split('\\n')[0] };\n }\n }),\n );\n\n const updated: LayerPackageUpdate[] = [];\n const unchecked: string[] = [];\n for (const entry of checked) {\n if (entry.to === undefined) {\n unchecked.push(entry.name);\n onProgress?.(`kept ${entry.name} ${entry.from}: ${entry.failure ?? 'no published version'}`);\n continue;\n }\n if (entry.to === entry.from) continue;\n updated.push({ name: entry.name, from: entry.from, to: entry.to });\n onProgress?.(`${entry.name} ${entry.from} -> ${entry.to}`);\n }\n return { updated, unchecked };\n}\n\n/** Installs selected layer packages into Pi's project-local `.pi/npm` store and verifies the retry. */\nexport async function ensureLayerPackages(\n options: EnsureLayerPackagesOptions,\n dependencies: LayerPackageInstallerDependencies = {},\n): Promise<LayerPackageResult> {\n const environment = options.environment ?? process.env;\n const resolvers = dependencies.resolvers ?? createLayerResolvers(options.repoRoot);\n const configured = configuredPackageSpecifiers(options.config, options.layers);\n const missing = configured.filter((specifier) => resolvePackageEntries(resolvers, specifier) === undefined);\n const catalog = readPackageCatalog(environment);\n const missingSources = npmSources(missing, catalog);\n const sources = npmSources(configured, catalog, true);\n if (sources.length === 0) return { installed: [], updated: [], unchecked: [] };\n\n const managedDirectory = path.dirname(managedPackageManifestPath(options.repoRoot));\n if (missing.length === 0 && !fs.existsSync(managedDirectory)) return { installed: [], updated: [], unchecked: [] };\n\n const registrySources = sources.filter((source) => !catalog.has(packageName(source)));\n const { updated, unchecked } = options.refresh\n ? await resolveUpdates(\n options.repoRoot,\n registrySources,\n dependencies.publishedVersion ?? createVersionReader(options.repoRoot, environment),\n options.onProgress,\n )\n : { updated: [], unchecked: [] };\n const targets = new Map<string, string>();\n for (const source of sources) {\n const name = packageName(source);\n const target = catalog.get(name)?.target;\n if (target) targets.set(name, target);\n }\n for (const update of updated) targets.set(update.name, update.to);\n const manifestChanged = ensureManagedPackageManifest(options.repoRoot, sources, targets);\n if (missing.length === 0 && !manifestChanged) return { installed: [], updated: [], unchecked };\n\n const packageManager = dependencies.packageManager ?? createPackageManager(options.repoRoot, environment);\n options.onProgress?.(\n `installing ${\n updated.length > 0\n ? pluralize(updated.length, 'update')\n : missing.length > 0\n ? pluralize(missing.length, 'missing package')\n : 'the package manifest'\n }`,\n );\n try {\n if (manifestChanged) {\n // Installing one source reconciles the complete dependency set already written\n // to package.json, avoiding one npm install and audit report per DoomPi package.\n await packageManager.install(sources[0]!, { local: true });\n } else {\n await packageManager.resolveExtensionSources(missingSources, { local: true });\n }\n } catch (error) {\n const attempted = manifestChanged ? sources : missingSources;\n throw new Error(`Failed to reconcile DoomPi layer package(s): ${attempted.join(', ')}`, { cause: error });\n }\n\n const unresolved = missing.filter((specifier) => !resolvePackageEntries(resolvers, specifier)?.length);\n if (unresolved.length > 0) {\n throw new Error(\n `Installed DoomPi layer package(s) did not expose the configured Pi extension: ${unresolved.join(', ')}`,\n );\n }\n return { installed: missingSources, updated, unchecked };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./syncRegistration.cjs"),n=require("./syncState.cjs"),r=require("./bootstrapLocator.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:os");o=e.__toESM(o,1);function s(e){let s=e.homeDirectory??o.default.homedir(),c=[],l,u;try{u=t.readSyncRegistration(e.repoRoot,s),l=u?n.readSyncState(e.repoRoot,s):void 0}catch{l=void 0,u=void 0}if(!l||!u)return{fresh:!1,reasons:[`never-synced`]};let d;try{d=n.computeInputsHash(e.repoRoot,l.selection,s)}catch{c.push(`configuration-changed`)}d!==void 0&&d!==l.inputsHash&&c.push(`configuration-changed`),l.webSourcesHash!==void 0&&n.computeWebSourcesHash(l.resolved)!==l.webSourcesHash&&c.push(`code-changed`);try{r.readBootstrapStatus(e.repoRoot,
|
|
1
|
+
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./syncRegistration.cjs"),n=require("./syncState.cjs"),r=require("./bootstrapLocator.cjs");let i=require("node:path");i=e.__toESM(i,1);let a=require("node:fs");a=e.__toESM(a,1);let o=require("node:os");o=e.__toESM(o,1);function s(e){let s=e.homeDirectory??o.default.homedir(),c=[],l,u;try{u=t.readSyncRegistration(e.repoRoot,s),l=u?n.readSyncState(e.repoRoot,s):void 0}catch{l=void 0,u=void 0}if(!l||!u)return{fresh:!1,reasons:[`never-synced`]};let d;try{d=n.computeInputsHash(e.repoRoot,l.selection,s)}catch{c.push(`configuration-changed`)}d!==void 0&&d!==l.inputsHash&&c.push(`configuration-changed`),l.webSourcesHash!==void 0&&n.computeWebSourcesHash(l.resolved)!==l.webSourcesHash&&c.push(`code-changed`);try{r.readBootstrapStatus(e.repoRoot,e.expectedBootstrapEntry,s).fresh||c.push(`runtime-stale`)}catch{c.push(`runtime-stale`)}return u.webDirectory!==null&&(!a.default.existsSync(i.default.join(u.webDirectory,`index.html`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`composition.js`))||!a.default.existsSync(i.default.join(i.default.dirname(u.webDirectory),`plugins`,`manifest.json`)))&&c.push(`cockpit-bundle-missing`),a.default.existsSync(u.apiDirectory)||c.push(`package-apis-missing`),{fresh:c.length===0,reasons:c,recordedInputsHash:l.inputsHash,...d===void 0?{}:{currentInputsHash:d}}}function c(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}exports.describeSyncDrift=c,exports.readSyncDrift=s;
|
|
2
2
|
//# sourceMappingURL=syncDrift.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncDrift.cjs","names":["os","readSyncRegistration","readSyncState","computeInputsHash","computeWebSourcesHash","readBootstrapStatus","fs","path"],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot,
|
|
1
|
+
{"version":3,"file":"syncDrift.cjs","names":["os","readSyncRegistration","readSyncState","computeInputsHash","computeWebSourcesHash","readBootstrapStatus","fs","path"],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json')))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"6RA6CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiBA,EAAAA,QAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAeC,EAAAA,qBAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAeC,EAAAA,cAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoBC,EAAAA,kBAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAaC,EAAAA,sBAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACGC,EAAAA,oBAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAWA,OATE,EAAa,eAAiB,OAC7B,CAACC,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAACD,EAAAA,QAAG,WAAWC,EAAAA,QAAK,KAAKA,EAAAA,QAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,IAE/F,EAAQ,KAAK,wBAAwB,EAElCD,EAAAA,QAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncDrift.d.cts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;
|
|
1
|
+
{"version":3,"file":"syncDrift.d.cts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAgE9C,kBAAkB,OAAO"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncDrift.d.mts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;
|
|
1
|
+
{"version":3,"file":"syncDrift.d.mts","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"mappings":";KAOY;UAQK;;EAEf;EACA,SAAS;;EAET;EACA;;UAGe;EACf;;EAEA;EACA;;;;;;;;;;;;;;;;iBAiBc,cAAc,SAAS,uBAAuB;;iBAgE9C,kBAAkB,OAAO"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{readSyncRegistration as e}from"./syncRegistration.mjs";import{computeInputsHash as t,computeWebSourcesHash as n,readSyncState as r}from"./syncState.mjs";import{readBootstrapStatus as i}from"./bootstrapLocator.mjs";import a from"node:path";import o from"node:fs";import s from"node:os";function c(c){let l=c.homeDirectory??s.homedir(),u=[],d,f;try{f=e(c.repoRoot,l),d=f?r(c.repoRoot,l):void 0}catch{d=void 0,f=void 0}if(!d||!f)return{fresh:!1,reasons:[`never-synced`]};let p;try{p=t(c.repoRoot,d.selection,l)}catch{u.push(`configuration-changed`)}p!==void 0&&p!==d.inputsHash&&u.push(`configuration-changed`),d.webSourcesHash!==void 0&&n(d.resolved)!==d.webSourcesHash&&u.push(`code-changed`);try{i(c.repoRoot,
|
|
1
|
+
import{readSyncRegistration as e}from"./syncRegistration.mjs";import{computeInputsHash as t,computeWebSourcesHash as n,readSyncState as r}from"./syncState.mjs";import{readBootstrapStatus as i}from"./bootstrapLocator.mjs";import a from"node:path";import o from"node:fs";import s from"node:os";function c(c){let l=c.homeDirectory??s.homedir(),u=[],d,f;try{f=e(c.repoRoot,l),d=f?r(c.repoRoot,l):void 0}catch{d=void 0,f=void 0}if(!d||!f)return{fresh:!1,reasons:[`never-synced`]};let p;try{p=t(c.repoRoot,d.selection,l)}catch{u.push(`configuration-changed`)}p!==void 0&&p!==d.inputsHash&&u.push(`configuration-changed`),d.webSourcesHash!==void 0&&n(d.resolved)!==d.webSourcesHash&&u.push(`code-changed`);try{i(c.repoRoot,c.expectedBootstrapEntry,l).fresh||u.push(`runtime-stale`)}catch{u.push(`runtime-stale`)}return f.webDirectory!==null&&(!o.existsSync(a.join(f.webDirectory,`index.html`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`composition.js`))||!o.existsSync(a.join(a.dirname(f.webDirectory),`plugins`,`manifest.json`)))&&u.push(`cockpit-bundle-missing`),o.existsSync(f.apiDirectory)||u.push(`package-apis-missing`),{fresh:u.length===0,reasons:u,recordedInputsHash:d.inputsHash,...p===void 0?{}:{currentInputsHash:p}}}function l(e){if(e.fresh)return`the repository is synced`;let t={"never-synced":`it has never been synced`,"configuration-changed":`its configuration changed since the last sync`,"code-changed":`its cockpit sources changed since the last sync`,"runtime-stale":`its precompiled runtime is out of date`,"cockpit-bundle-missing":`the cockpit bundle is missing`,"package-apis-missing":`the package API routes are missing`};return e.reasons.map(e=>t[e]).join(`, `)}export{l as describeSyncDrift,c as readSyncDrift};
|
|
2
2
|
//# sourceMappingURL=syncDrift.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"syncDrift.mjs","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot,
|
|
1
|
+
{"version":3,"file":"syncDrift.mjs","names":[],"sources":["../../../src/adapters/syncDrift.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readBootstrapStatus } from './bootstrapLocator.ts';\nimport { computeInputsHash, computeWebSourcesHash, readSyncState, type SyncState } from './syncState.ts';\nimport { readSyncRegistration } from './syncRegistration.ts';\n\nexport type SyncDriftReason =\n | 'never-synced'\n | 'configuration-changed'\n | 'code-changed'\n | 'runtime-stale'\n | 'cockpit-bundle-missing'\n | 'package-apis-missing';\n\nexport interface SyncDrift {\n /** True when nothing needs syncing before a session starts. */\n fresh: boolean;\n reasons: SyncDriftReason[];\n /** The recorded inputs hash, for a caller that wants to log what moved. */\n recordedInputsHash?: string;\n currentInputsHash?: string;\n}\n\nexport interface ReadSyncDriftOptions {\n repoRoot: string;\n /** Launcher-owned Doom entry used to build this repository's generated bootstrap. */\n expectedBootstrapEntry?: string;\n homeDirectory?: string;\n}\n\n/**\n * Whether this repository is synced for the composition it would launch.\n *\n * Sync produces three things a session or the cockpit reads later: the\n * resolved composition, the plugin bundle the browser loads, and the package\n * API routes each host mounts. A session started against any of them stale\n * runs, but quietly wrong: the cockpit shows no plugin surfaces and package\n * APIs answer nothing, with nothing in the log to say why.\n *\n * The hash is the one sync already records. It covers every `.doom` document\n * in both the repository and the home directory, the MCP configuration, the\n * plugin catalogue, and each profile's persona files, so an edit to any of\n * them reads as drift without this having to know what changed.\n */\nexport function readSyncDrift(options: ReadSyncDriftOptions): SyncDrift {\n const homeDirectory = options.homeDirectory ?? os.homedir();\n const reasons: SyncDriftReason[] = [];\n\n let state: SyncState | undefined;\n let registration: ReturnType<typeof readSyncRegistration>;\n try {\n registration = readSyncRegistration(options.repoRoot, homeDirectory);\n state = registration ? readSyncState(options.repoRoot, homeDirectory) : undefined;\n } catch {\n // Unreadable or invalid registration is indistinguishable from absent for\n // this purpose: the next session needs a sync before it can be trusted.\n state = undefined;\n registration = undefined;\n }\n if (!state || !registration) return { fresh: false, reasons: ['never-synced'] };\n\n let currentInputsHash: string | undefined;\n try {\n currentInputsHash = computeInputsHash(options.repoRoot, state.selection, homeDirectory);\n } catch {\n // A malformed document cannot be hashed, and syncing is what reports it.\n reasons.push('configuration-changed');\n }\n if (currentInputsHash !== undefined && currentInputsHash !== state.inputsHash) {\n reasons.push('configuration-changed');\n }\n // Cockpit sources are compiled straight from each package's web/ folder, so a\n // rebuilt plugin surface is a real change that no configuration hash sees. A\n // state recorded before this was tracked has nothing to compare and is left\n // alone rather than being called stale on sight.\n if (state.webSourcesHash !== undefined && computeWebSourcesHash(state.resolved) !== state.webSourcesHash) {\n reasons.push('code-changed');\n }\n // The same question the `--check` path asks, so the cockpit and the CLI\n // cannot disagree about whether one repository is synced.\n try {\n if (!readBootstrapStatus(options.repoRoot, options.expectedBootstrapEntry, homeDirectory).fresh) {\n reasons.push('runtime-stale');\n }\n } catch {\n // An unreadable bootstrap record is exactly as unusable as a stale one, and\n // syncing is what reports the underlying cause.\n reasons.push('runtime-stale');\n }\n if (\n registration.webDirectory !== null &&\n (!fs.existsSync(path.join(registration.webDirectory, 'index.html')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'composition.js')) ||\n !fs.existsSync(path.join(path.dirname(registration.webDirectory), 'plugins', 'manifest.json')))\n ) {\n reasons.push('cockpit-bundle-missing');\n }\n if (!fs.existsSync(registration.apiDirectory)) reasons.push('package-apis-missing');\n\n return {\n fresh: reasons.length === 0,\n reasons,\n recordedInputsHash: state.inputsHash,\n ...(currentInputsHash === undefined ? {} : { currentInputsHash }),\n };\n}\n\n/** A one-line account of what drifted, for a log the person running the hub reads. */\nexport function describeSyncDrift(drift: SyncDrift): string {\n if (drift.fresh) return 'the repository is synced';\n const detail: Record<SyncDriftReason, string> = {\n 'never-synced': 'it has never been synced',\n 'configuration-changed': 'its configuration changed since the last sync',\n 'code-changed': 'its cockpit sources changed since the last sync',\n 'runtime-stale': 'its precompiled runtime is out of date',\n 'cockpit-bundle-missing': 'the cockpit bundle is missing',\n 'package-apis-missing': 'the package API routes are missing',\n };\n return drift.reasons.map((reason) => detail[reason]).join(', ');\n}\n"],"mappings":"oSA6CA,SAAgB,EAAc,EAA0C,CACtE,IAAM,EAAgB,EAAQ,eAAiB,EAAG,QAAQ,EACpD,EAA6B,CAAC,EAEhC,EACA,EACJ,GAAI,CACF,EAAe,EAAqB,EAAQ,SAAU,CAAa,EACnE,EAAQ,EAAe,EAAc,EAAQ,SAAU,CAAa,EAAI,IAAA,EAC1E,MAAQ,CAGN,EAAQ,IAAA,GACR,EAAe,IAAA,EACjB,CACA,GAAI,CAAC,GAAS,CAAC,EAAc,MAAO,CAAE,MAAO,GAAO,QAAS,CAAC,cAAc,CAAE,EAE9E,IAAI,EACJ,GAAI,CACF,EAAoB,EAAkB,EAAQ,SAAU,EAAM,UAAW,CAAa,CACxF,MAAQ,CAEN,EAAQ,KAAK,uBAAuB,CACtC,CACI,IAAsB,IAAA,IAAa,IAAsB,EAAM,YACjE,EAAQ,KAAK,uBAAuB,EAMlC,EAAM,iBAAmB,IAAA,IAAa,EAAsB,EAAM,QAAQ,IAAM,EAAM,gBACxF,EAAQ,KAAK,cAAc,EAI7B,GAAI,CACG,EAAoB,EAAQ,SAAU,EAAQ,uBAAwB,CAAa,CAAC,CAAC,OACxF,EAAQ,KAAK,eAAe,CAEhC,MAAQ,CAGN,EAAQ,KAAK,eAAe,CAC9B,CAWA,OATE,EAAa,eAAiB,OAC7B,CAAC,EAAG,WAAW,EAAK,KAAK,EAAa,aAAc,YAAY,CAAC,GAChE,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,gBAAgB,CAAC,GAC9F,CAAC,EAAG,WAAW,EAAK,KAAK,EAAK,QAAQ,EAAa,YAAY,EAAG,UAAW,eAAe,CAAC,IAE/F,EAAQ,KAAK,wBAAwB,EAElC,EAAG,WAAW,EAAa,YAAY,GAAG,EAAQ,KAAK,sBAAsB,EAE3E,CACL,MAAO,EAAQ,SAAW,EAC1B,UACA,mBAAoB,EAAM,WAC1B,GAAI,IAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,mBAAkB,CACjE,CACF,CAGA,SAAgB,EAAkB,EAA0B,CAC1D,GAAI,EAAM,MAAO,MAAO,2BACxB,IAAM,EAA0C,CAC9C,eAAgB,2BAChB,wBAAyB,gDACzB,eAAgB,kDAChB,gBAAiB,yCACjB,yBAA0B,gCAC1B,uBAAwB,oCAC1B,EACA,OAAO,EAAM,QAAQ,IAAK,GAAW,EAAO,EAAO,CAAC,CAAC,KAAK,IAAI,CAChE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/doompi",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.53",
|
|
4
4
|
"description": "Opinionated, composable Pi distribution for scoped agent tools, skills, MCP servers, and developer workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -319,17 +319,17 @@
|
|
|
319
319
|
"rolldown": "1.2.6",
|
|
320
320
|
"typebox": "1.3.19",
|
|
321
321
|
"yaml": "2.9.0",
|
|
322
|
-
"@agimon-ai/doompi-autostop": "0.0.1-alpha.
|
|
323
|
-
"@agimon-ai/doompi-
|
|
324
|
-
"@agimon-ai/doompi-
|
|
325
|
-
"@agimon-ai/doompi-
|
|
326
|
-
"@agimon-ai/doompi-
|
|
327
|
-
"@agimon-ai/doompi-
|
|
328
|
-
"@agimon-ai/doompi-
|
|
329
|
-
"@agimon-ai/doompi-
|
|
330
|
-
"@agimon-ai/doompi-
|
|
331
|
-
"@agimon-ai/doompi-
|
|
332
|
-
"@agimon-ai/doompi-
|
|
322
|
+
"@agimon-ai/doompi-autostop": "0.0.1-alpha.30",
|
|
323
|
+
"@agimon-ai/doompi-domain": "0.0.1-alpha.31",
|
|
324
|
+
"@agimon-ai/doompi-cache": "0.0.1-alpha.19",
|
|
325
|
+
"@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.52",
|
|
326
|
+
"@agimon-ai/doompi-config": "0.0.1-alpha.52",
|
|
327
|
+
"@agimon-ai/doompi-notification": "0.0.1-alpha.30",
|
|
328
|
+
"@agimon-ai/doompi-profile": "0.0.1-alpha.31",
|
|
329
|
+
"@agimon-ai/doompi-ui": "0.0.1-alpha.53",
|
|
330
|
+
"@agimon-ai/doompi-telemetry": "0.0.1-alpha.52",
|
|
331
|
+
"@agimon-ai/doompi-skill": "0.0.1-alpha.31",
|
|
332
|
+
"@agimon-ai/doompi-major-mode": "0.0.1-alpha.31"
|
|
333
333
|
},
|
|
334
334
|
"devDependencies": {
|
|
335
335
|
"@earendil-works/pi-ai": "0.84.4",
|
|
@@ -339,7 +339,7 @@
|
|
|
339
339
|
"@vitest/coverage-v8": "4.1.11",
|
|
340
340
|
"typescript": "7.0.2",
|
|
341
341
|
"vitest": "4.1.11",
|
|
342
|
-
"@agimon-ai/doompi-runner": "0.0.1-alpha.
|
|
342
|
+
"@agimon-ai/doompi-runner": "0.0.1-alpha.53"
|
|
343
343
|
},
|
|
344
344
|
"peerDependencies": {
|
|
345
345
|
"@earendil-works/pi-tui": "0.84.4"
|