@agimon-ai/doompi 0.0.1-alpha.74 → 0.0.1-alpha.77
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/builders/apiContractExport/index.cjs +2 -1
- package/dist/builders/apiContractExport/index.cjs.map +1 -1
- package/dist/builders/apiContractExport/index.mjs +2 -1
- package/dist/builders/apiContractExport/index.mjs.map +1 -1
- package/dist/builders/cli/bootstrapLocator/index.cjs +49 -16
- package/dist/builders/cli/bootstrapLocator/index.cjs.map +1 -1
- package/dist/builders/cli/bootstrapLocator/index.d.cts.map +1 -1
- package/dist/builders/cli/bootstrapLocator/index.d.mts.map +1 -1
- package/dist/builders/cli/bootstrapLocator/index.mjs +48 -16
- package/dist/builders/cli/bootstrapLocator/index.mjs.map +1 -1
- package/dist/builders/cli/piExtensionDispatcher/index.cjs +14 -5
- package/dist/builders/cli/piExtensionDispatcher/index.cjs.map +1 -1
- package/dist/builders/cli/piExtensionDispatcher/index.mjs +15 -6
- package/dist/builders/cli/piExtensionDispatcher/index.mjs.map +1 -1
- package/dist/builders/server/runtime.cjs +10 -0
- package/dist/builders/server/runtime.cjs.map +1 -1
- package/dist/builders/server/runtime.mjs +11 -1
- package/dist/builders/server/runtime.mjs.map +1 -1
- package/dist/cli/commands/sync/help.cjs +5 -2
- package/dist/cli/commands/sync/help.cjs.map +1 -1
- package/dist/cli/commands/sync/help.mjs +5 -2
- package/dist/cli/commands/sync/help.mjs.map +1 -1
- package/dist/cli/commands/sync/index.cjs +54 -31
- package/dist/cli/commands/sync/index.cjs.map +1 -1
- package/dist/cli/commands/sync/index.d.cts +12 -0
- package/dist/cli/commands/sync/index.d.cts.map +1 -1
- package/dist/cli/commands/sync/index.d.mts +12 -0
- package/dist/cli/commands/sync/index.d.mts.map +1 -1
- package/dist/cli/commands/sync/index.mjs +53 -33
- package/dist/cli/commands/sync/index.mjs.map +1 -1
- package/dist/cli/commands/sync/prepare.cjs +6 -9
- package/dist/cli/commands/sync/prepare.cjs.map +1 -1
- package/dist/cli/commands/sync/prepare.mjs +6 -8
- package/dist/cli/commands/sync/prepare.mjs.map +1 -1
- package/dist/cli/commands/sync/workflow.cjs +21 -22
- package/dist/cli/commands/sync/workflow.cjs.map +1 -1
- package/dist/cli/commands/sync/workflow.mjs +16 -17
- package/dist/cli/commands/sync/workflow.mjs.map +1 -1
- package/dist/cli/server/run.cjs +8 -1
- package/dist/cli/server/run.cjs.map +1 -1
- package/dist/cli/server/run.mjs +8 -1
- package/dist/cli/server/run.mjs.map +1 -1
- package/dist/compiler/index.cjs +19 -0
- package/dist/compiler/index.cjs.map +1 -1
- package/dist/compiler/index.mjs +19 -0
- package/dist/compiler/index.mjs.map +1 -1
- package/dist/composition/syncDrift/index.cjs +42 -14
- package/dist/composition/syncDrift/index.cjs.map +1 -1
- package/dist/composition/syncDrift/index.d.cts +2 -0
- package/dist/composition/syncDrift/index.d.cts.map +1 -1
- package/dist/composition/syncDrift/index.d.mts +2 -0
- package/dist/composition/syncDrift/index.d.mts.map +1 -1
- package/dist/composition/syncDrift/index.mjs +43 -16
- package/dist/composition/syncDrift/index.mjs.map +1 -1
- package/dist/sync-command.cjs +3 -0
- package/dist/sync-command.d.cts +2 -2
- package/dist/sync-command.d.mts +2 -2
- package/dist/sync-command.mjs +2 -2
- package/package.json +17 -16
|
@@ -20,7 +20,8 @@ function exportApiContracts(options) {
|
|
|
20
20
|
const read = (root) => {
|
|
21
21
|
const drift = require_index$1.readSyncDrift({
|
|
22
22
|
repoRoot: root,
|
|
23
|
-
homeDirectory: options.homeDirectory
|
|
23
|
+
homeDirectory: options.homeDirectory,
|
|
24
|
+
requireFreshSources: false
|
|
24
25
|
});
|
|
25
26
|
if (!drift.fresh) throw new Error(`Run doompi sync for '${root}' before exporting API contracts: ${drift.reasons.join(", ")}`);
|
|
26
27
|
const registration = (0, _agimon_ai_doompi_core_sync_registration.readSyncRegistration)(root, options.homeDirectory);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["globalDoomConfigDirectory","resolveDoomConfigurationRoot","readSyncDrift","readSyncRegistration","parseDoomServerBundle","fs","path","crypto","parseCompiledContracts","loadMajorModesConfig","resolveLayers","createApiDocuments","canonicalContractJson"],"sources":["../../../src/builders/apiContractExport/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport {\n canonicalContractJson,\n createApiDocuments,\n parseCompiledContracts,\n type ApiContractSelection,\n type ApiDocuments,\n} from '@agimon-ai/doompi-core/api-contracts';\nimport { parseDoomServerBundle } from '@agimon-ai/doompi-core/server-facet';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\n\nimport { resolveDoomConfigurationRoot } from '../../composition/repository';\nimport { readSyncDrift } from '../../composition/syncDrift';\n\n/** Read only published generations. Export must never implicitly sync or activate a facet. */\nexport function exportApiContracts(options: {\n cwd: string;\n homeDirectory: string;\n outputDirectory: string;\n majorMode?: string;\n}): ApiDocuments['manifest'] {\n const globalRoot = globalDoomConfigDirectory(options.homeDirectory);\n const workspaceRoot = resolveDoomConfigurationRoot(options.cwd, options.homeDirectory);\n const read = (root: string) => {\n const drift = readSyncDrift({
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["globalDoomConfigDirectory","resolveDoomConfigurationRoot","readSyncDrift","readSyncRegistration","parseDoomServerBundle","fs","path","crypto","parseCompiledContracts","loadMajorModesConfig","resolveLayers","createApiDocuments","canonicalContractJson"],"sources":["../../../src/builders/apiContractExport/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport {\n canonicalContractJson,\n createApiDocuments,\n parseCompiledContracts,\n type ApiContractSelection,\n type ApiDocuments,\n} from '@agimon-ai/doompi-core/api-contracts';\nimport { parseDoomServerBundle } from '@agimon-ai/doompi-core/server-facet';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\n\nimport { resolveDoomConfigurationRoot } from '../../composition/repository';\nimport { readSyncDrift } from '../../composition/syncDrift';\n\n/** Read only published generations. Export must never implicitly sync or activate a facet. */\nexport function exportApiContracts(options: {\n cwd: string;\n homeDirectory: string;\n outputDirectory: string;\n majorMode?: string;\n}): ApiDocuments['manifest'] {\n const globalRoot = globalDoomConfigDirectory(options.homeDirectory);\n const workspaceRoot = resolveDoomConfigurationRoot(options.cwd, options.homeDirectory);\n const read = (root: string) => {\n const drift = readSyncDrift({\n repoRoot: root,\n homeDirectory: options.homeDirectory,\n requireFreshSources: false,\n });\n if (!drift.fresh)\n throw new Error(`Run doompi sync for '${root}' before exporting API contracts: ${drift.reasons.join(', ')}`);\n const registration = readSyncRegistration(root, options.homeDirectory);\n if (!registration?.serverBundle) throw new Error(`No synced server bundle for '${root}'`);\n const descriptor = parseDoomServerBundle(JSON.parse(fs.readFileSync(registration.serverBundle.path, 'utf8')));\n if (!descriptor.contracts) throw new Error(`Resync '${root}' to generate API contracts`);\n const file = path.resolve(path.dirname(registration.serverBundle.path), descriptor.contracts.file);\n const bytes = fs.readFileSync(file);\n if (crypto.createHash('sha256').update(bytes).digest('hex') !== descriptor.contracts.sha256)\n throw new Error(`API contract checksum mismatch for '${root}'`);\n return {\n compiled: parseCompiledContracts(JSON.parse(bytes.toString('utf8'))),\n config: loadMajorModesConfig(root, options.homeDirectory),\n };\n };\n const global = read(globalRoot);\n const workspace = globalRoot === workspaceRoot ? global : read(workspaceRoot);\n const sessionMode = options.majorMode ?? workspace.config.defaultMajorMode;\n const selection = (\n source: typeof global,\n scope: ApiContractSelection['scope'],\n majorMode: string,\n ): ApiContractSelection => ({\n scope,\n majorMode,\n activeLayers: resolveLayers(source.config, majorMode),\n compiled: source.compiled,\n });\n // A typo must not silently export an empty session surface.\n if (!Object.hasOwn(workspace.config.majorMode, sessionMode)) throw new Error(`Unknown major mode '${sessionMode}'`);\n const documents = createApiDocuments([\n selection(global, 'global', global.config.defaultMajorMode),\n selection(workspace, 'workspace', workspace.config.defaultMajorMode),\n selection(workspace, 'session', sessionMode),\n ]);\n const files = {\n 'openapi.json': canonicalContractJson(documents.openapi),\n 'asyncapi.json': canonicalContractJson(documents.asyncapi),\n };\n const manifest = {\n ...documents.manifest,\n artifacts: Object.fromEntries(\n Object.entries(files).map(([name, bytes]) => [\n name,\n {\n sha256: crypto.createHash('sha256').update(bytes).digest('hex'),\n },\n ]),\n ),\n };\n fs.mkdirSync(options.outputDirectory, { recursive: true });\n // Write the manifest last. Consumers verify both checksums before accepting the bundle.\n for (const [name, bytes] of Object.entries({ ...files, 'manifest.json': canonicalContractJson(manifest) })) {\n const target = path.join(options.outputDirectory, name);\n const temporary = `${target}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, bytes, { flag: 'wx' });\n fs.renameSync(temporary, target);\n } finally {\n fs.rmSync(temporary, { force: true });\n }\n }\n return documents.manifest;\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAoBA,SAAgB,mBAAmB,SAKN;CAC3B,MAAM,cAAA,GAAaA,gCAAAA,0BAAAA,CAA0B,QAAQ,aAAa;CAClE,MAAM,gBAAgBC,cAAAA,6BAA6B,QAAQ,KAAK,QAAQ,aAAa;CACrF,MAAM,QAAQ,SAAiB;EAC7B,MAAM,QAAQC,gBAAAA,cAAc;GAC1B,UAAU;GACV,eAAe,QAAQ;GACvB,qBAAqB;EACvB,CAAC;EACD,IAAI,CAAC,MAAM,OACT,MAAM,IAAI,MAAM,wBAAwB,KAAK,oCAAoC,MAAM,QAAQ,KAAK,IAAI,GAAG;EAC7G,MAAM,gBAAA,GAAeC,yCAAAA,qBAAAA,CAAqB,MAAM,QAAQ,aAAa;EACrE,IAAI,CAAC,cAAc,cAAc,MAAM,IAAI,MAAM,gCAAgC,KAAK,EAAE;EACxF,MAAM,cAAA,GAAaC,oCAAAA,sBAAAA,CAAsB,KAAK,MAAMC,QAAAA,QAAG,aAAa,aAAa,aAAa,MAAM,MAAM,CAAC,CAAC;EAC5G,IAAI,CAAC,WAAW,WAAW,MAAM,IAAI,MAAM,WAAW,KAAK,4BAA4B;EACvF,MAAM,OAAOC,UAAAA,QAAK,QAAQA,UAAAA,QAAK,QAAQ,aAAa,aAAa,IAAI,GAAG,WAAW,UAAU,IAAI;EACjG,MAAM,QAAQD,QAAAA,QAAG,aAAa,IAAI;EAClC,IAAIE,YAAAA,QAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,MAAM,WAAW,UAAU,QACnF,MAAM,IAAI,MAAM,uCAAuC,KAAK,EAAE;EAChE,OAAO;GACL,WAAA,GAAUC,qCAAAA,uBAAAA,CAAuB,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC,CAAC;GACnE,SAAA,GAAQC,oCAAAA,qBAAAA,CAAqB,MAAM,QAAQ,aAAa;EAC1D;CACF;CACA,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,YAAY,eAAe,gBAAgB,SAAS,KAAK,aAAa;CAC5E,MAAM,cAAc,QAAQ,aAAa,UAAU,OAAO;CAC1D,MAAM,aACJ,QACA,OACA,eAC0B;EAC1B;EACA;EACA,eAAA,GAAcC,oCAAAA,cAAAA,CAAc,OAAO,QAAQ,SAAS;EACpD,UAAU,OAAO;CACnB;CAEA,IAAI,CAAC,OAAO,OAAO,UAAU,OAAO,WAAW,WAAW,GAAG,MAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;CAClH,MAAM,aAAA,GAAYC,qCAAAA,mBAAAA,CAAmB;EACnC,UAAU,QAAQ,UAAU,OAAO,OAAO,gBAAgB;EAC1D,UAAU,WAAW,aAAa,UAAU,OAAO,gBAAgB;EACnE,UAAU,WAAW,WAAW,WAAW;CAC7C,CAAC;CACD,MAAM,QAAQ;EACZ,iBAAA,GAAgBC,qCAAAA,sBAAAA,CAAsB,UAAU,OAAO;EACvD,kBAAA,GAAiBA,qCAAAA,sBAAAA,CAAsB,UAAU,QAAQ;CAC3D;CACA,MAAM,WAAW;EACf,GAAG,UAAU;EACb,WAAW,OAAO,YAChB,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAC3C,MACA,EACE,QAAQL,YAAAA,QAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,EAChE,CACF,CAAC,CACH;CACF;CACA,QAAA,QAAG,UAAU,QAAQ,iBAAiB,EAAE,WAAW,KAAK,CAAC;CAEzD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ;EAAE,GAAG;EAAO,kBAAA,GAAiBK,qCAAAA,sBAAAA,CAAsB,QAAQ;CAAE,CAAC,GAAG;EAC1G,MAAM,SAASN,UAAAA,QAAK,KAAK,QAAQ,iBAAiB,IAAI;EACtD,MAAM,YAAY,GAAG,OAAO,GAAGC,YAAAA,QAAO,WAAW,EAAE;EACnD,IAAI;GACF,QAAA,QAAG,cAAc,WAAW,OAAO,EAAE,MAAM,KAAK,CAAC;GACjD,QAAA,QAAG,WAAW,WAAW,MAAM;EACjC,UAAU;GACR,QAAA,QAAG,OAAO,WAAW,EAAE,OAAO,KAAK,CAAC;EACtC;CACF;CACA,OAAO,UAAU;AACnB"}
|
|
@@ -16,7 +16,8 @@ function exportApiContracts(options) {
|
|
|
16
16
|
const read = (root) => {
|
|
17
17
|
const drift = readSyncDrift({
|
|
18
18
|
repoRoot: root,
|
|
19
|
-
homeDirectory: options.homeDirectory
|
|
19
|
+
homeDirectory: options.homeDirectory,
|
|
20
|
+
requireFreshSources: false
|
|
20
21
|
});
|
|
21
22
|
if (!drift.fresh) throw new Error(`Run doompi sync for '${root}' before exporting API contracts: ${drift.reasons.join(", ")}`);
|
|
22
23
|
const registration = readSyncRegistration(root, options.homeDirectory);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/builders/apiContractExport/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport {\n canonicalContractJson,\n createApiDocuments,\n parseCompiledContracts,\n type ApiContractSelection,\n type ApiDocuments,\n} from '@agimon-ai/doompi-core/api-contracts';\nimport { parseDoomServerBundle } from '@agimon-ai/doompi-core/server-facet';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\n\nimport { resolveDoomConfigurationRoot } from '../../composition/repository';\nimport { readSyncDrift } from '../../composition/syncDrift';\n\n/** Read only published generations. Export must never implicitly sync or activate a facet. */\nexport function exportApiContracts(options: {\n cwd: string;\n homeDirectory: string;\n outputDirectory: string;\n majorMode?: string;\n}): ApiDocuments['manifest'] {\n const globalRoot = globalDoomConfigDirectory(options.homeDirectory);\n const workspaceRoot = resolveDoomConfigurationRoot(options.cwd, options.homeDirectory);\n const read = (root: string) => {\n const drift = readSyncDrift({
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/builders/apiContractExport/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nimport { globalDoomConfigDirectory } from '@agimon-ai/doompi-config/config';\nimport { loadMajorModesConfig, resolveLayers } from '@agimon-ai/doompi-config/majorModes';\nimport {\n canonicalContractJson,\n createApiDocuments,\n parseCompiledContracts,\n type ApiContractSelection,\n type ApiDocuments,\n} from '@agimon-ai/doompi-core/api-contracts';\nimport { parseDoomServerBundle } from '@agimon-ai/doompi-core/server-facet';\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\n\nimport { resolveDoomConfigurationRoot } from '../../composition/repository';\nimport { readSyncDrift } from '../../composition/syncDrift';\n\n/** Read only published generations. Export must never implicitly sync or activate a facet. */\nexport function exportApiContracts(options: {\n cwd: string;\n homeDirectory: string;\n outputDirectory: string;\n majorMode?: string;\n}): ApiDocuments['manifest'] {\n const globalRoot = globalDoomConfigDirectory(options.homeDirectory);\n const workspaceRoot = resolveDoomConfigurationRoot(options.cwd, options.homeDirectory);\n const read = (root: string) => {\n const drift = readSyncDrift({\n repoRoot: root,\n homeDirectory: options.homeDirectory,\n requireFreshSources: false,\n });\n if (!drift.fresh)\n throw new Error(`Run doompi sync for '${root}' before exporting API contracts: ${drift.reasons.join(', ')}`);\n const registration = readSyncRegistration(root, options.homeDirectory);\n if (!registration?.serverBundle) throw new Error(`No synced server bundle for '${root}'`);\n const descriptor = parseDoomServerBundle(JSON.parse(fs.readFileSync(registration.serverBundle.path, 'utf8')));\n if (!descriptor.contracts) throw new Error(`Resync '${root}' to generate API contracts`);\n const file = path.resolve(path.dirname(registration.serverBundle.path), descriptor.contracts.file);\n const bytes = fs.readFileSync(file);\n if (crypto.createHash('sha256').update(bytes).digest('hex') !== descriptor.contracts.sha256)\n throw new Error(`API contract checksum mismatch for '${root}'`);\n return {\n compiled: parseCompiledContracts(JSON.parse(bytes.toString('utf8'))),\n config: loadMajorModesConfig(root, options.homeDirectory),\n };\n };\n const global = read(globalRoot);\n const workspace = globalRoot === workspaceRoot ? global : read(workspaceRoot);\n const sessionMode = options.majorMode ?? workspace.config.defaultMajorMode;\n const selection = (\n source: typeof global,\n scope: ApiContractSelection['scope'],\n majorMode: string,\n ): ApiContractSelection => ({\n scope,\n majorMode,\n activeLayers: resolveLayers(source.config, majorMode),\n compiled: source.compiled,\n });\n // A typo must not silently export an empty session surface.\n if (!Object.hasOwn(workspace.config.majorMode, sessionMode)) throw new Error(`Unknown major mode '${sessionMode}'`);\n const documents = createApiDocuments([\n selection(global, 'global', global.config.defaultMajorMode),\n selection(workspace, 'workspace', workspace.config.defaultMajorMode),\n selection(workspace, 'session', sessionMode),\n ]);\n const files = {\n 'openapi.json': canonicalContractJson(documents.openapi),\n 'asyncapi.json': canonicalContractJson(documents.asyncapi),\n };\n const manifest = {\n ...documents.manifest,\n artifacts: Object.fromEntries(\n Object.entries(files).map(([name, bytes]) => [\n name,\n {\n sha256: crypto.createHash('sha256').update(bytes).digest('hex'),\n },\n ]),\n ),\n };\n fs.mkdirSync(options.outputDirectory, { recursive: true });\n // Write the manifest last. Consumers verify both checksums before accepting the bundle.\n for (const [name, bytes] of Object.entries({ ...files, 'manifest.json': canonicalContractJson(manifest) })) {\n const target = path.join(options.outputDirectory, name);\n const temporary = `${target}.${crypto.randomUUID()}.tmp`;\n try {\n fs.writeFileSync(temporary, bytes, { flag: 'wx' });\n fs.renameSync(temporary, target);\n } finally {\n fs.rmSync(temporary, { force: true });\n }\n }\n return documents.manifest;\n}\n"],"mappings":";;;;;;;;;;;;AAoBA,SAAgB,mBAAmB,SAKN;CAC3B,MAAM,aAAa,0BAA0B,QAAQ,aAAa;CAClE,MAAM,gBAAgB,6BAA6B,QAAQ,KAAK,QAAQ,aAAa;CACrF,MAAM,QAAQ,SAAiB;EAC7B,MAAM,QAAQ,cAAc;GAC1B,UAAU;GACV,eAAe,QAAQ;GACvB,qBAAqB;EACvB,CAAC;EACD,IAAI,CAAC,MAAM,OACT,MAAM,IAAI,MAAM,wBAAwB,KAAK,oCAAoC,MAAM,QAAQ,KAAK,IAAI,GAAG;EAC7G,MAAM,eAAe,qBAAqB,MAAM,QAAQ,aAAa;EACrE,IAAI,CAAC,cAAc,cAAc,MAAM,IAAI,MAAM,gCAAgC,KAAK,EAAE;EACxF,MAAM,aAAa,sBAAsB,KAAK,MAAM,GAAG,aAAa,aAAa,aAAa,MAAM,MAAM,CAAC,CAAC;EAC5G,IAAI,CAAC,WAAW,WAAW,MAAM,IAAI,MAAM,WAAW,KAAK,4BAA4B;EACvF,MAAM,OAAO,KAAK,QAAQ,KAAK,QAAQ,aAAa,aAAa,IAAI,GAAG,WAAW,UAAU,IAAI;EACjG,MAAM,QAAQ,GAAG,aAAa,IAAI;EAClC,IAAI,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,MAAM,WAAW,UAAU,QACnF,MAAM,IAAI,MAAM,uCAAuC,KAAK,EAAE;EAChE,OAAO;GACL,UAAU,uBAAuB,KAAK,MAAM,MAAM,SAAS,MAAM,CAAC,CAAC;GACnE,QAAQ,qBAAqB,MAAM,QAAQ,aAAa;EAC1D;CACF;CACA,MAAM,SAAS,KAAK,UAAU;CAC9B,MAAM,YAAY,eAAe,gBAAgB,SAAS,KAAK,aAAa;CAC5E,MAAM,cAAc,QAAQ,aAAa,UAAU,OAAO;CAC1D,MAAM,aACJ,QACA,OACA,eAC0B;EAC1B;EACA;EACA,cAAc,cAAc,OAAO,QAAQ,SAAS;EACpD,UAAU,OAAO;CACnB;CAEA,IAAI,CAAC,OAAO,OAAO,UAAU,OAAO,WAAW,WAAW,GAAG,MAAM,IAAI,MAAM,uBAAuB,YAAY,EAAE;CAClH,MAAM,YAAY,mBAAmB;EACnC,UAAU,QAAQ,UAAU,OAAO,OAAO,gBAAgB;EAC1D,UAAU,WAAW,aAAa,UAAU,OAAO,gBAAgB;EACnE,UAAU,WAAW,WAAW,WAAW;CAC7C,CAAC;CACD,MAAM,QAAQ;EACZ,gBAAgB,sBAAsB,UAAU,OAAO;EACvD,iBAAiB,sBAAsB,UAAU,QAAQ;CAC3D;CACA,MAAM,WAAW;EACf,GAAG,UAAU;EACb,WAAW,OAAO,YAChB,OAAO,QAAQ,KAAK,CAAC,CAAC,KAAK,CAAC,MAAM,WAAW,CAC3C,MACA,EACE,QAAQ,OAAO,WAAW,QAAQ,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,OAAO,KAAK,EAChE,CACF,CAAC,CACH;CACF;CACA,GAAG,UAAU,QAAQ,iBAAiB,EAAE,WAAW,KAAK,CAAC;CAEzD,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ;EAAE,GAAG;EAAO,iBAAiB,sBAAsB,QAAQ;CAAE,CAAC,GAAG;EAC1G,MAAM,SAAS,KAAK,KAAK,QAAQ,iBAAiB,IAAI;EACtD,MAAM,YAAY,GAAG,OAAO,GAAG,OAAO,WAAW,EAAE;EACnD,IAAI;GACF,GAAG,cAAc,WAAW,OAAO,EAAE,MAAM,KAAK,CAAC;GACjD,GAAG,WAAW,WAAW,MAAM;EACjC,UAAU;GACR,GAAG,OAAO,WAAW,EAAE,OAAO,KAAK,CAAC;EACtC;CACF;CACA,OAAO,UAAU;AACnB"}
|
|
@@ -10,9 +10,12 @@ node_path = require_runtime.__toESM(node_path, 1);
|
|
|
10
10
|
let _agimon_ai_doompi_core_sync_registration = require("@agimon-ai/doompi-core/sync-registration");
|
|
11
11
|
let node_os = require("node:os");
|
|
12
12
|
node_os = require_runtime.__toESM(node_os, 1);
|
|
13
|
+
let node_crypto = require("node:crypto");
|
|
14
|
+
node_crypto = require_runtime.__toESM(node_crypto, 1);
|
|
13
15
|
let _agimon_ai_doompi_core_sync_state_contract = require("@agimon-ai/doompi-core/sync-state-contract");
|
|
14
16
|
//#region src/builders/cli/bootstrapLocator/index.ts
|
|
15
17
|
const BOOTSTRAP_ENTRY_ENV = "DOOMPI_BOOTSTRAP_ENTRY";
|
|
18
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
16
19
|
function isRecord(value) {
|
|
17
20
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18
21
|
}
|
|
@@ -34,6 +37,11 @@ function isInside(directory, target) {
|
|
|
34
37
|
const relative = node_path.default.relative(canonicalPath(directory), canonicalPath(target));
|
|
35
38
|
return relative === "" || !relative.startsWith("..") && !node_path.default.isAbsolute(relative);
|
|
36
39
|
}
|
|
40
|
+
function parseFingerprints(value) {
|
|
41
|
+
if (!Array.isArray(value)) return void 0;
|
|
42
|
+
const fingerprints = value.map(require_inputs.parseInputFingerprint);
|
|
43
|
+
return fingerprints.some((fingerprint) => fingerprint === void 0) ? void 0 : fingerprints;
|
|
44
|
+
}
|
|
37
45
|
function packagedDoomEntry() {
|
|
38
46
|
return process.env[BOOTSTRAP_ENTRY_ENV] || require_entryResolution.ownEntry("composedPi");
|
|
39
47
|
}
|
|
@@ -79,13 +87,10 @@ function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
|
79
87
|
if (!isInside(generatedDirectory, manifestPath)) return void 0;
|
|
80
88
|
try {
|
|
81
89
|
const parsed = JSON.parse(node_fs.default.readFileSync(manifestPath, "utf8"));
|
|
82
|
-
if (!isRecord(parsed) || typeof parsed.output !== "string" || !Array.isArray(parsed.artifacts) || parsed.artifacts.some((artifact) => typeof artifact !== "string") || !Array.isArray(parsed.entries) || parsed.entries.some((entry) => typeof entry !== "string") || !Array.isArray(parsed.inputs)) return;
|
|
83
|
-
const inputs =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (fingerprint === void 0) return void 0;
|
|
87
|
-
inputs.push(fingerprint);
|
|
88
|
-
}
|
|
90
|
+
if (!isRecord(parsed) || typeof parsed.output !== "string" || !Array.isArray(parsed.artifacts) || parsed.artifacts.some((artifact) => typeof artifact !== "string") || !Array.isArray(parsed.entries) || parsed.entries.some((entry) => typeof entry !== "string") || !Array.isArray(parsed.inputs) || parsed.artifactInputs !== void 0 && !Array.isArray(parsed.artifactInputs)) return;
|
|
91
|
+
const inputs = parseFingerprints(parsed.inputs);
|
|
92
|
+
const artifactInputs = parsed.artifactInputs === void 0 ? void 0 : parseFingerprints(parsed.artifactInputs);
|
|
93
|
+
if (!inputs || parsed.artifactInputs !== void 0 && !artifactInputs) return void 0;
|
|
89
94
|
const artifacts = parsed.artifacts;
|
|
90
95
|
const entries = parsed.entries;
|
|
91
96
|
if (!isInside(generatedDirectory, parsed.output) || artifacts.some((artifact) => !isInside(generatedDirectory, artifact))) return;
|
|
@@ -93,23 +98,51 @@ function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
|
93
98
|
output: parsed.output,
|
|
94
99
|
artifacts,
|
|
95
100
|
entries,
|
|
96
|
-
inputs
|
|
101
|
+
inputs,
|
|
102
|
+
...artifactInputs ? { artifactInputs } : {}
|
|
97
103
|
};
|
|
98
104
|
} catch {
|
|
99
105
|
return;
|
|
100
106
|
}
|
|
101
107
|
}
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
function artifactInputsAreIntact(manifest, generatedDirectory) {
|
|
109
|
+
try {
|
|
110
|
+
if (!manifest.artifactInputs || manifest.artifactInputs.length === 0) return false;
|
|
111
|
+
const expected = new Set([manifest.output, ...manifest.artifacts].map(canonicalPath));
|
|
112
|
+
const seen = /* @__PURE__ */ new Set();
|
|
113
|
+
for (const input of manifest.artifactInputs) {
|
|
114
|
+
if (!SHA256.test(input.sha256) || !isInside(generatedDirectory, input.path)) return false;
|
|
115
|
+
const target = canonicalPath(input.path);
|
|
116
|
+
if (!expected.has(target)) return false;
|
|
117
|
+
const stat = node_fs.default.statSync(target);
|
|
118
|
+
if (!stat.isFile() || stat.size !== input.size) return false;
|
|
119
|
+
if (node_crypto.default.createHash("sha256").update(node_fs.default.readFileSync(target)).digest("hex") !== input.sha256) return false;
|
|
120
|
+
seen.add(target);
|
|
121
|
+
}
|
|
122
|
+
return seen.size === expected.size;
|
|
123
|
+
} catch {
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
function compilerManifestIsUsable(manifest, generatedDirectory) {
|
|
128
|
+
return artifactInputsAreIntact(manifest, generatedDirectory);
|
|
105
129
|
}
|
|
106
|
-
function
|
|
130
|
+
function compilerManifestIsFresh(manifest, generatedDirectory) {
|
|
131
|
+
return compilerManifestIsUsable(manifest, generatedDirectory) && require_inputs.inputsAreFresh(manifest.inputs);
|
|
132
|
+
}
|
|
133
|
+
function bootstrapRecord(state, expectedBootstrapEntry, requireFreshInputs) {
|
|
107
134
|
if (!state.bootstrap || !state.precompile) return void 0;
|
|
108
135
|
if (state.precompile.version !== _agimon_ai_doompi_core_sync_state_contract.PRECOMPILE_STATE_VERSION || canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)) return;
|
|
109
136
|
const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);
|
|
110
|
-
if (!record || !compilerManifestIsFresh(record) || record.output !== state.bootstrap || record.entries.length !== 1 || canonicalPath(record.entries[0] ?? "") !== canonicalPath(expectedBootstrapEntry)) return;
|
|
137
|
+
if (!record || !compilerManifestIsUsable(record, state.generatedDirectory) || requireFreshInputs && !compilerManifestIsFresh(record, state.generatedDirectory) || record.output !== state.bootstrap || record.entries.length !== 1 || canonicalPath(record.entries[0] ?? "") !== canonicalPath(expectedBootstrapEntry)) return;
|
|
111
138
|
return record;
|
|
112
139
|
}
|
|
140
|
+
function usableBootstrapRecord(state, expectedBootstrapEntry) {
|
|
141
|
+
return bootstrapRecord(state, expectedBootstrapEntry, false);
|
|
142
|
+
}
|
|
143
|
+
function freshBootstrapRecord(state, expectedBootstrapEntry) {
|
|
144
|
+
return bootstrapRecord(state, expectedBootstrapEntry, true);
|
|
145
|
+
}
|
|
113
146
|
/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */
|
|
114
147
|
function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = node_os.default.homedir()) {
|
|
115
148
|
const state = readBootstrapState(repoRoot, homeDirectory);
|
|
@@ -120,7 +153,7 @@ function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirect
|
|
|
120
153
|
};
|
|
121
154
|
return {
|
|
122
155
|
bootstrap: state.bootstrap,
|
|
123
|
-
fresh:
|
|
156
|
+
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
124
157
|
};
|
|
125
158
|
}
|
|
126
159
|
/** Validates one selected composition without touching any inactive bundle inputs. */
|
|
@@ -135,7 +168,7 @@ function readBundleStatus(repoRoot, compositionFingerprint, homeDirectory = node
|
|
|
135
168
|
const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);
|
|
136
169
|
return {
|
|
137
170
|
bundle,
|
|
138
|
-
fresh: Boolean(manifest && manifest.output === bundle &&
|
|
171
|
+
fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsUsable(manifest, state.generatedDirectory))
|
|
139
172
|
};
|
|
140
173
|
}
|
|
141
174
|
/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */
|
|
@@ -155,7 +188,7 @@ function readBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = n
|
|
|
155
188
|
const manifestPath = state.precompile?.bundleManifests[fingerprint];
|
|
156
189
|
if (!manifestPath) return false;
|
|
157
190
|
const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);
|
|
158
|
-
return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));
|
|
191
|
+
return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest, state.generatedDirectory));
|
|
159
192
|
});
|
|
160
193
|
return {
|
|
161
194
|
bootstrap: state.bootstrap,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","ownEntry","readSyncRegistration","os","resolveDoomConfigurationRoot","readSyncState","BUNDLED_PRECOMPILE_STRATEGY","parseInputFingerprint","inputsAreFresh","PRECOMPILE_STATE_VERSION"],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/sync-state-contract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncState } from '../../../composition/syncState';\nimport { ownEntry } from '../entryResolution';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\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(): string {\n return process.env[BOOTSTRAP_ENTRY_ENV] || ownEntry('composedPi');\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":";;;;;;;;;;;;;;AAYA,MAAM,sBAAsB;AAiC5B,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,aAAa,OAAwC;CAC5D,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,CAAC;CAC9B,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,UAAqC,OAAO,MAAM,OAAO,QAAQ,CACjG;AACF;AAEA,SAAS,cAAc,QAAwB;CAC7C,MAAM,WAAWA,UAAAA,QAAK,QAAQ,MAAM;CACpC,IAAI;EACF,OAAOC,QAAAA,QAAG,aAAa,QAAQ;CACjC,QAAQ;EACN,MAAM,SAASD,UAAAA,QAAK,QAAQ,QAAQ;EACpC,IAAI,WAAW,UAAU,OAAO;EAChC,OAAOA,UAAAA,QAAK,KAAK,cAAc,MAAM,GAAGA,UAAAA,QAAK,SAAS,QAAQ,CAAC;CACjE;AACF;AAEA,SAAS,SAAS,WAAmB,QAAyB;CAC5D,MAAM,WAAWA,UAAAA,QAAK,SAAS,cAAc,SAAS,GAAG,cAAc,MAAM,CAAC;CAC9E,OAAO,aAAa,MAAO,CAAC,SAAS,WAAW,IAAI,KAAK,CAACA,UAAAA,QAAK,WAAW,QAAQ;AACpF;AAEA,SAAS,oBAA4B;CACnC,OAAO,QAAQ,IAAI,wBAAwBE,wBAAAA,SAAS,YAAY;AAClE;AAEA,SAAS,iBAAiB,UAAkB,eAAgC;CAC1E,QAAA,GAAOC,yCAAAA,qBAAAA,CAAqB,UAAU,aAAa,MAAM,KAAA;AAC3D;;AAGA,SAAgB,eAAe,KAAa,gBAAwBC,QAAAA,QAAG,QAAQ,GAAuB;CACpG,IAAI,iBAAiB,KAAK,aAAa,GAAG,OAAO,cAAc,GAAG;CAClE,MAAM,OAAOC,cAAAA,6BAA6B,KAAK,aAAa;CAC5D,OAAO,iBAAiB,MAAM,aAAa,IAAI,cAAc,IAAI,IAAI,KAAA;AACvE;AAEA,SAAS,mBAAmB,UAAkB,gBAAwBD,QAAAA,QAAG,QAAQ,GAA+B;CAC9G,MAAM,gBAAA,GAAeD,yCAAAA,qBAAAA,CAAqB,UAAU,aAAa;CACjE,IAAI,CAAC,cAAc,OAAO,KAAA;CAC1B,MAAM,YAAY,aAAa;CAC/B,MAAM,qBAAqB,aAAa;CACxC,MAAM,QAAQG,gBAAAA,cAAc,UAAU,aAAa;CACnD,IAAI,CAAC,OAAO,OAAO,KAAA;CACnB,MAAM,YAAY,OAAO,MAAM,cAAc,WAAW,MAAM,YAAY,KAAA;CAC1E,IAAI,aAAa,CAAC,SAAS,oBAAoB,SAAS,GACtD,MAAM,IAAI,MAAM,mCAAmC,mBAAmB,IAAI,WAAW;CAGvF,IAAI;CACJ,IAAI,MAAM,eAAe,KAAA,GAAW;EAClC,MAAM,QAAQ,MAAM;EACpB,IACE,CAAC,SAAS,KAAK,KACf,OAAO,MAAM,YAAY,YACzB,MAAM,aAAaC,2CAAAA,+BACnB,OAAO,MAAM,mBAAmB,YAChC,OAAO,MAAM,sBAAsB,YACnC,CAAC,SAAS,MAAM,eAAe,KAC/B,OAAO,OAAO,MAAM,eAAe,CAAC,CAAC,MAAM,aAAa,OAAO,aAAa,QAAQ,GAEpF,MAAM,IAAI,MAAM,sBAAsB,UAAU,kCAAkC;EAEpF,aAAa;GACX,SAAS,MAAM;GACf,UAAU,MAAM;GAChB,gBAAgB,MAAM;GACtB,mBAAmB,MAAM;GACzB,iBAAiB,MAAM;EACzB;CACF;CAEA,OAAO;EACL;EACA;EACA;EACA,SAAS,aAAa,MAAM,OAAO;EACnC;CACF;AACF;AAEA,SAAS,qBAAqB,cAAsB,oBAA0D;CAC5G,IAAI,CAAC,SAAS,oBAAoB,YAAY,GAAG,OAAO,KAAA;CACxD,IAAI;EACF,MAAM,SAAS,KAAK,MAAMN,QAAAA,QAAG,aAAa,cAAc,MAAM,CAAC;EAC/D,IACE,CAAC,SAAS,MAAM,KAChB,OAAO,OAAO,WAAW,YACzB,CAAC,MAAM,QAAQ,OAAO,SAAS,KAC/B,OAAO,UAAU,MAAM,aAAa,OAAO,aAAa,QAAQ,KAChE,CAAC,MAAM,QAAQ,OAAO,OAAO,KAC7B,OAAO,QAAQ,MAAM,UAAU,OAAO,UAAU,QAAQ,KACxD,CAAC,MAAM,QAAQ,OAAO,MAAM,GAE5B;EAEF,MAAM,SAA6B,CAAC;EACpC,KAAK,MAAM,SAAS,OAAO,QAAQ;GAGjC,MAAM,cAAcO,eAAAA,sBAAsB,KAAK;GAC/C,IAAI,gBAAgB,KAAA,GAAW,OAAO,KAAA;GACtC,OAAO,KAAK,WAAW;EACzB;EACA,MAAM,YAAY,OAAO;EACzB,MAAM,UAAU,OAAO;EACvB,IACE,CAAC,SAAS,oBAAoB,OAAO,MAAM,KAC3C,UAAU,MAAM,aAAa,CAAC,SAAS,oBAAoB,QAAQ,CAAC,GAEpE;EAEF,OAAO;GAAE,QAAQ,OAAO;GAAQ;GAAW;GAAS;EAAO;CAC7D,QAAQ;EACN;CACF;AACF;AAEA,SAAS,wBAAwB,UAAqC;CACpE,IAAI,CAACP,QAAAA,QAAG,WAAW,SAAS,MAAM,KAAK,SAAS,UAAU,MAAM,aAAa,CAACA,QAAAA,QAAG,WAAW,QAAQ,CAAC,GAAG,OAAO;CAC/G,OAAOQ,eAAAA,eAAe,SAAS,MAAM;AACvC;AAEA,SAAS,qBAAqB,OAAuB,wBAA8D;CACjH,IAAI,CAAC,MAAM,aAAa,CAAC,MAAM,YAAY,OAAO,KAAA;CAClD,IACE,MAAM,WAAW,YAAYC,2CAAAA,4BAC7B,cAAc,MAAM,WAAW,cAAc,MAAM,cAAc,sBAAsB,GAEvF;CAEF,MAAM,SAAS,qBAAqB,MAAM,WAAW,mBAAmB,MAAM,kBAAkB;CAChG,IACE,CAAC,UACD,CAAC,wBAAwB,MAAM,KAC/B,OAAO,WAAW,MAAM,aACxB,OAAO,QAAQ,WAAW,KAC1B,cAAc,OAAO,QAAQ,MAAM,EAAE,MAAM,cAAc,sBAAsB,GAE/E;CAEF,OAAO;AACT;;AAGA,SAAgB,2BACd,UACA,wBACA,gBAAwBN,QAAAA,QAAG,QAAQ,GAClB;CACjB,MAAM,QAAQ,mBAAmB,UAAU,aAAa;CACxD,MAAM,WAAW,0BAA0B,kBAAkB;CAC7D,IAAI,CAAC,OAAO,OAAO;EAAE,WAAW,KAAA;EAAW,OAAO;CAAM;CACxD,OAAO;EACL,WAAW,MAAM;EACjB,OAAO,qBAAqB,OAAO,QAAQ,MAAM,KAAA;CACnD;AACF;;AAGA,SAAgB,iBACd,UACA,wBACA,gBAAwBA,QAAAA,QAAG,QAAQ,GACrB;CACd,MAAM,QAAQ,mBAAmB,UAAU,aAAa;CACxD,MAAM,SAAS,OAAO,QAAQ;CAC9B,MAAM,eAAe,OAAO,YAAY,gBAAgB;CACxD,IAAI,CAAC,SAAS,MAAM,YAAY,YAAYM,2CAAAA,4BAA4B,CAAC,UAAU,CAAC,cAClF,OAAO;EAAE;EAAQ,OAAO;CAAM;CAEhC,MAAM,WAAW,qBAAqB,cAAc,MAAM,kBAAkB;CAC5E,OAAO;EACL;EACA,OAAO,QAAQ,YAAY,SAAS,WAAW,UAAU,wBAAwB,QAAQ,CAAC;CAC5F;AACF;;AAGA,SAAgB,oBACd,UACA,wBACA,gBAAwBN,QAAAA,QAAG,QAAQ,GAClB;CACjB,MAAM,QAAQ,mBAAmB,UAAU,aAAa;CACxD,MAAM,WAAW,0BAA0B,kBAAkB;CAC7D,IAAI,CAAC,OAAO,aAAa,CAAC,MAAM,cAAc,CAAC,qBAAqB,OAAO,QAAQ,GACjF,OAAO;EAAE,WAAW,OAAO;EAAW,OAAO;CAAM;CAErD,MAAM,YAAY,OAAO,OAAO,MAAM,WAAW,eAAe;CAChE,IAAI,IAAI,IAAI,SAAS,CAAC,CAAC,SAAS,UAAU,QAAQ,OAAO;EAAE,WAAW,MAAM;EAAW,OAAO;CAAM;CACpG,MAAM,QAAQ,OAAO,QAAQ,MAAM,OAAO,CAAC,CAAC,OAAO,CAAC,aAAa,YAAY;EAC3E,MAAM,eAAe,MAAM,YAAY,gBAAgB;EACvD,IAAI,CAAC,cAAc,OAAO;EAC1B,MAAM,WAAW,qBAAqB,cAAc,MAAM,kBAAkB;EAC5E,OAAO,QAAQ,YAAY,SAAS,WAAW,UAAU,wBAAwB,QAAQ,CAAC;CAC5F,CAAC;CACD,OAAO;EAAE,WAAW,MAAM;EAAW;CAAM;AAC7C"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","parseInputFingerprint","ownEntry","readSyncRegistration","os","resolveDoomConfigurationRoot","readSyncState","BUNDLED_PRECOMPILE_STRATEGY","crypto","inputsAreFresh","PRECOMPILE_STATE_VERSION"],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/sync-state-contract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncState } from '../../../composition/syncState';\nimport { ownEntry } from '../entryResolution';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\nconst SHA256 = /^[a-f0-9]{64}$/u;\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n artifactInputs?: 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 parseFingerprints(value: unknown): InputFingerprint[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const fingerprints = value.map(parseInputFingerprint);\n return fingerprints.some((fingerprint) => fingerprint === undefined)\n ? undefined\n : (fingerprints as InputFingerprint[]);\n}\n\nfunction packagedDoomEntry(): string {\n return process.env[BOOTSTRAP_ENTRY_ENV] || ownEntry('composedPi');\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 (parsed.artifactInputs !== undefined && !Array.isArray(parsed.artifactInputs))\n ) {\n return undefined;\n }\n const inputs = parseFingerprints(parsed.inputs);\n const artifactInputs = parsed.artifactInputs === undefined ? undefined : parseFingerprints(parsed.artifactInputs);\n if (!inputs || (parsed.artifactInputs !== undefined && !artifactInputs)) return undefined;\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, ...(artifactInputs ? { artifactInputs } : {}) };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactInputsAreIntact(manifest: CompilerManifest, generatedDirectory: string): boolean {\n try {\n if (!manifest.artifactInputs || manifest.artifactInputs.length === 0) return false;\n const expected = new Set([manifest.output, ...manifest.artifacts].map(canonicalPath));\n const seen = new Set<string>();\n for (const input of manifest.artifactInputs) {\n if (!SHA256.test(input.sha256) || !isInside(generatedDirectory, input.path)) return false;\n const target = canonicalPath(input.path);\n if (!expected.has(target)) return false;\n const stat = fs.statSync(target);\n if (!stat.isFile() || stat.size !== input.size) return false;\n if (crypto.createHash('sha256').update(fs.readFileSync(target)).digest('hex') !== input.sha256) return false;\n seen.add(target);\n }\n return seen.size === expected.size;\n } catch {\n return false;\n }\n}\n\nfunction compilerManifestIsUsable(manifest: CompilerManifest, generatedDirectory: string): boolean {\n return artifactInputsAreIntact(manifest, generatedDirectory);\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest, generatedDirectory: string): boolean {\n return compilerManifestIsUsable(manifest, generatedDirectory) && inputsAreFresh(manifest.inputs);\n}\n\nfunction bootstrapRecord(\n state: BootstrapState,\n expectedBootstrapEntry: string,\n requireFreshInputs: boolean,\n): 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 !compilerManifestIsUsable(record, state.generatedDirectory) ||\n (requireFreshInputs && !compilerManifestIsFresh(record, state.generatedDirectory)) ||\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\nfunction usableBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n return bootstrapRecord(state, expectedBootstrapEntry, false);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n return bootstrapRecord(state, expectedBootstrapEntry, true);\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: usableBootstrapRecord(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(\n manifest && manifest.output === bundle && compilerManifestIsUsable(manifest, state.generatedDirectory),\n ),\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(\n manifest && manifest.output === bundle && compilerManifestIsFresh(manifest, state.generatedDirectory),\n );\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":"index.d.cts","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"mappings":";;wBA4FgB,eAAe,aAAa"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"mappings":";;wBA4FgB,eAAe,aAAa"}
|
|
@@ -6,9 +6,11 @@ import fs from "node:fs";
|
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { readSyncRegistration } from "@agimon-ai/doompi-core/sync-registration";
|
|
8
8
|
import os from "node:os";
|
|
9
|
+
import crypto from "node:crypto";
|
|
9
10
|
import { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from "@agimon-ai/doompi-core/sync-state-contract";
|
|
10
11
|
//#region src/builders/cli/bootstrapLocator/index.ts
|
|
11
12
|
const BOOTSTRAP_ENTRY_ENV = "DOOMPI_BOOTSTRAP_ENTRY";
|
|
13
|
+
const SHA256 = /^[a-f0-9]{64}$/u;
|
|
12
14
|
function isRecord(value) {
|
|
13
15
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
14
16
|
}
|
|
@@ -30,6 +32,11 @@ function isInside(directory, target) {
|
|
|
30
32
|
const relative = path.relative(canonicalPath(directory), canonicalPath(target));
|
|
31
33
|
return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
|
|
32
34
|
}
|
|
35
|
+
function parseFingerprints(value) {
|
|
36
|
+
if (!Array.isArray(value)) return void 0;
|
|
37
|
+
const fingerprints = value.map(parseInputFingerprint);
|
|
38
|
+
return fingerprints.some((fingerprint) => fingerprint === void 0) ? void 0 : fingerprints;
|
|
39
|
+
}
|
|
33
40
|
function packagedDoomEntry() {
|
|
34
41
|
return process.env[BOOTSTRAP_ENTRY_ENV] || ownEntry("composedPi");
|
|
35
42
|
}
|
|
@@ -75,13 +82,10 @@ function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
|
75
82
|
if (!isInside(generatedDirectory, manifestPath)) return void 0;
|
|
76
83
|
try {
|
|
77
84
|
const parsed = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
78
|
-
if (!isRecord(parsed) || typeof parsed.output !== "string" || !Array.isArray(parsed.artifacts) || parsed.artifacts.some((artifact) => typeof artifact !== "string") || !Array.isArray(parsed.entries) || parsed.entries.some((entry) => typeof entry !== "string") || !Array.isArray(parsed.inputs)) return;
|
|
79
|
-
const inputs =
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (fingerprint === void 0) return void 0;
|
|
83
|
-
inputs.push(fingerprint);
|
|
84
|
-
}
|
|
85
|
+
if (!isRecord(parsed) || typeof parsed.output !== "string" || !Array.isArray(parsed.artifacts) || parsed.artifacts.some((artifact) => typeof artifact !== "string") || !Array.isArray(parsed.entries) || parsed.entries.some((entry) => typeof entry !== "string") || !Array.isArray(parsed.inputs) || parsed.artifactInputs !== void 0 && !Array.isArray(parsed.artifactInputs)) return;
|
|
86
|
+
const inputs = parseFingerprints(parsed.inputs);
|
|
87
|
+
const artifactInputs = parsed.artifactInputs === void 0 ? void 0 : parseFingerprints(parsed.artifactInputs);
|
|
88
|
+
if (!inputs || parsed.artifactInputs !== void 0 && !artifactInputs) return void 0;
|
|
85
89
|
const artifacts = parsed.artifacts;
|
|
86
90
|
const entries = parsed.entries;
|
|
87
91
|
if (!isInside(generatedDirectory, parsed.output) || artifacts.some((artifact) => !isInside(generatedDirectory, artifact))) return;
|
|
@@ -89,23 +93,51 @@ function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
|
89
93
|
output: parsed.output,
|
|
90
94
|
artifacts,
|
|
91
95
|
entries,
|
|
92
|
-
inputs
|
|
96
|
+
inputs,
|
|
97
|
+
...artifactInputs ? { artifactInputs } : {}
|
|
93
98
|
};
|
|
94
99
|
} catch {
|
|
95
100
|
return;
|
|
96
101
|
}
|
|
97
102
|
}
|
|
98
|
-
function
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
function artifactInputsAreIntact(manifest, generatedDirectory) {
|
|
104
|
+
try {
|
|
105
|
+
if (!manifest.artifactInputs || manifest.artifactInputs.length === 0) return false;
|
|
106
|
+
const expected = new Set([manifest.output, ...manifest.artifacts].map(canonicalPath));
|
|
107
|
+
const seen = /* @__PURE__ */ new Set();
|
|
108
|
+
for (const input of manifest.artifactInputs) {
|
|
109
|
+
if (!SHA256.test(input.sha256) || !isInside(generatedDirectory, input.path)) return false;
|
|
110
|
+
const target = canonicalPath(input.path);
|
|
111
|
+
if (!expected.has(target)) return false;
|
|
112
|
+
const stat = fs.statSync(target);
|
|
113
|
+
if (!stat.isFile() || stat.size !== input.size) return false;
|
|
114
|
+
if (crypto.createHash("sha256").update(fs.readFileSync(target)).digest("hex") !== input.sha256) return false;
|
|
115
|
+
seen.add(target);
|
|
116
|
+
}
|
|
117
|
+
return seen.size === expected.size;
|
|
118
|
+
} catch {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function compilerManifestIsUsable(manifest, generatedDirectory) {
|
|
123
|
+
return artifactInputsAreIntact(manifest, generatedDirectory);
|
|
101
124
|
}
|
|
102
|
-
function
|
|
125
|
+
function compilerManifestIsFresh(manifest, generatedDirectory) {
|
|
126
|
+
return compilerManifestIsUsable(manifest, generatedDirectory) && inputsAreFresh(manifest.inputs);
|
|
127
|
+
}
|
|
128
|
+
function bootstrapRecord(state, expectedBootstrapEntry, requireFreshInputs) {
|
|
103
129
|
if (!state.bootstrap || !state.precompile) return void 0;
|
|
104
130
|
if (state.precompile.version !== PRECOMPILE_STATE_VERSION || canonicalPath(state.precompile.bootstrapEntry) !== canonicalPath(expectedBootstrapEntry)) return;
|
|
105
131
|
const record = readCompilerManifest(state.precompile.bootstrapManifest, state.generatedDirectory);
|
|
106
|
-
if (!record || !compilerManifestIsFresh(record) || record.output !== state.bootstrap || record.entries.length !== 1 || canonicalPath(record.entries[0] ?? "") !== canonicalPath(expectedBootstrapEntry)) return;
|
|
132
|
+
if (!record || !compilerManifestIsUsable(record, state.generatedDirectory) || requireFreshInputs && !compilerManifestIsFresh(record, state.generatedDirectory) || record.output !== state.bootstrap || record.entries.length !== 1 || canonicalPath(record.entries[0] ?? "") !== canonicalPath(expectedBootstrapEntry)) return;
|
|
107
133
|
return record;
|
|
108
134
|
}
|
|
135
|
+
function usableBootstrapRecord(state, expectedBootstrapEntry) {
|
|
136
|
+
return bootstrapRecord(state, expectedBootstrapEntry, false);
|
|
137
|
+
}
|
|
138
|
+
function freshBootstrapRecord(state, expectedBootstrapEntry) {
|
|
139
|
+
return bootstrapRecord(state, expectedBootstrapEntry, true);
|
|
140
|
+
}
|
|
109
141
|
/** Validates only the bootstrap graph needed before the generated bootstrap is imported. */
|
|
110
142
|
function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = os.homedir()) {
|
|
111
143
|
const state = readBootstrapState(repoRoot, homeDirectory);
|
|
@@ -116,7 +148,7 @@ function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirect
|
|
|
116
148
|
};
|
|
117
149
|
return {
|
|
118
150
|
bootstrap: state.bootstrap,
|
|
119
|
-
fresh:
|
|
151
|
+
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
120
152
|
};
|
|
121
153
|
}
|
|
122
154
|
/** Validates one selected composition without touching any inactive bundle inputs. */
|
|
@@ -131,7 +163,7 @@ function readBundleStatus(repoRoot, compositionFingerprint, homeDirectory = os.h
|
|
|
131
163
|
const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);
|
|
132
164
|
return {
|
|
133
165
|
bundle,
|
|
134
|
-
fresh: Boolean(manifest && manifest.output === bundle &&
|
|
166
|
+
fresh: Boolean(manifest && manifest.output === bundle && compilerManifestIsUsable(manifest, state.generatedDirectory))
|
|
135
167
|
};
|
|
136
168
|
}
|
|
137
169
|
/** Validates the bootstrap and every bundle for `doompi sync --check` diagnostics. */
|
|
@@ -151,7 +183,7 @@ function readBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = o
|
|
|
151
183
|
const manifestPath = state.precompile?.bundleManifests[fingerprint];
|
|
152
184
|
if (!manifestPath) return false;
|
|
153
185
|
const manifest = readCompilerManifest(manifestPath, state.generatedDirectory);
|
|
154
|
-
return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest));
|
|
186
|
+
return Boolean(manifest && manifest.output === bundle && compilerManifestIsFresh(manifest, state.generatedDirectory));
|
|
155
187
|
});
|
|
156
188
|
return {
|
|
157
189
|
bootstrap: state.bootstrap,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/sync-state-contract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncState } from '../../../composition/syncState';\nimport { ownEntry } from '../entryResolution';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\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(): string {\n return process.env[BOOTSTRAP_ENTRY_ENV] || ownEntry('composedPi');\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":"index.mjs","names":[],"sources":["../../../../src/builders/cli/bootstrapLocator/index.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\n\nimport { readSyncRegistration } from '@agimon-ai/doompi-core/sync-registration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/sync-state-contract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readSyncState } from '../../../composition/syncState';\nimport { ownEntry } from '../entryResolution';\n\nconst BOOTSTRAP_ENTRY_ENV = 'DOOMPI_BOOTSTRAP_ENTRY';\nconst SHA256 = /^[a-f0-9]{64}$/u;\n\ninterface CompilerManifest {\n output: string;\n artifacts: string[];\n entries: string[];\n inputs: InputFingerprint[];\n artifactInputs?: 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 parseFingerprints(value: unknown): InputFingerprint[] | undefined {\n if (!Array.isArray(value)) return undefined;\n const fingerprints = value.map(parseInputFingerprint);\n return fingerprints.some((fingerprint) => fingerprint === undefined)\n ? undefined\n : (fingerprints as InputFingerprint[]);\n}\n\nfunction packagedDoomEntry(): string {\n return process.env[BOOTSTRAP_ENTRY_ENV] || ownEntry('composedPi');\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 (parsed.artifactInputs !== undefined && !Array.isArray(parsed.artifactInputs))\n ) {\n return undefined;\n }\n const inputs = parseFingerprints(parsed.inputs);\n const artifactInputs = parsed.artifactInputs === undefined ? undefined : parseFingerprints(parsed.artifactInputs);\n if (!inputs || (parsed.artifactInputs !== undefined && !artifactInputs)) return undefined;\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, ...(artifactInputs ? { artifactInputs } : {}) };\n } catch {\n return undefined;\n }\n}\n\nfunction artifactInputsAreIntact(manifest: CompilerManifest, generatedDirectory: string): boolean {\n try {\n if (!manifest.artifactInputs || manifest.artifactInputs.length === 0) return false;\n const expected = new Set([manifest.output, ...manifest.artifacts].map(canonicalPath));\n const seen = new Set<string>();\n for (const input of manifest.artifactInputs) {\n if (!SHA256.test(input.sha256) || !isInside(generatedDirectory, input.path)) return false;\n const target = canonicalPath(input.path);\n if (!expected.has(target)) return false;\n const stat = fs.statSync(target);\n if (!stat.isFile() || stat.size !== input.size) return false;\n if (crypto.createHash('sha256').update(fs.readFileSync(target)).digest('hex') !== input.sha256) return false;\n seen.add(target);\n }\n return seen.size === expected.size;\n } catch {\n return false;\n }\n}\n\nfunction compilerManifestIsUsable(manifest: CompilerManifest, generatedDirectory: string): boolean {\n return artifactInputsAreIntact(manifest, generatedDirectory);\n}\n\nfunction compilerManifestIsFresh(manifest: CompilerManifest, generatedDirectory: string): boolean {\n return compilerManifestIsUsable(manifest, generatedDirectory) && inputsAreFresh(manifest.inputs);\n}\n\nfunction bootstrapRecord(\n state: BootstrapState,\n expectedBootstrapEntry: string,\n requireFreshInputs: boolean,\n): 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 !compilerManifestIsUsable(record, state.generatedDirectory) ||\n (requireFreshInputs && !compilerManifestIsFresh(record, state.generatedDirectory)) ||\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\nfunction usableBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n return bootstrapRecord(state, expectedBootstrapEntry, false);\n}\n\nfunction freshBootstrapRecord(state: BootstrapState, expectedBootstrapEntry: string): CompilerManifest | undefined {\n return bootstrapRecord(state, expectedBootstrapEntry, true);\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: usableBootstrapRecord(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(\n manifest && manifest.output === bundle && compilerManifestIsUsable(manifest, state.generatedDirectory),\n ),\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(\n manifest && manifest.output === bundle && compilerManifestIsFresh(manifest, state.generatedDirectory),\n );\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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"}
|
|
@@ -15,7 +15,7 @@ function dispatcherManifest() {
|
|
|
15
15
|
name: _agimon_ai_doompi_core_doom_package.DOOM_PACKAGE_NAME,
|
|
16
16
|
private: true,
|
|
17
17
|
type: "module",
|
|
18
|
-
doompiDispatcher:
|
|
18
|
+
doompiDispatcher: 3,
|
|
19
19
|
pi: { extensions: [`./${DISPATCHER_ENTRY}`] }
|
|
20
20
|
}, null, 2)}\n`;
|
|
21
21
|
}
|
|
@@ -27,6 +27,8 @@ import path from 'node:path';
|
|
|
27
27
|
import { pathToFileURL } from 'node:url';
|
|
28
28
|
|
|
29
29
|
const REGISTRATION_VERSION = ${String(_agimon_ai_doompi_core_sync_registration.SYNC_REGISTRATION_VERSION)};
|
|
30
|
+
const LEGACY_REGISTRATION_VERSION = ${String(_agimon_ai_doompi_core_sync_registration.LEGACY_SYNC_REGISTRATION_VERSION)};
|
|
31
|
+
const API_VERSION = ${String(_agimon_ai_doompi_core_sync_registration.DOOMPI_API_VERSION)};
|
|
30
32
|
const PACKAGE_NAME = ${JSON.stringify(_agimon_ai_doompi_core_doom_package.DOOM_PACKAGE_NAME)};
|
|
31
33
|
const WARNING = 'warning';
|
|
32
34
|
|
|
@@ -89,7 +91,7 @@ function registration(root) {
|
|
|
89
91
|
const ids = identity(root);
|
|
90
92
|
const recordPath = path.join(os.homedir(), '.pi', '.doom', 'sync', 'registrations', ids.repositoryId, \`\${ids.worktreeId}.json\`);
|
|
91
93
|
const value = JSON.parse(fs.readFileSync(recordPath, 'utf8'));
|
|
92
|
-
if (value.version !== REGISTRATION_VERSION || canonical(value.root) !== root) throw new Error('registration identity mismatch');
|
|
94
|
+
if ((value.version !== REGISTRATION_VERSION && value.version !== LEGACY_REGISTRATION_VERSION) || canonical(value.root) !== root) throw new Error('registration identity mismatch');
|
|
93
95
|
if (value.identity?.repositoryId !== ids.repositoryId || value.identity?.worktreeId !== ids.worktreeId) throw new Error('registration worktree mismatch');
|
|
94
96
|
if (!inside(value.generationRoot, value.statePath) || !inside(value.package.root, value.package.entry)) throw new Error('registration path escapes its owner');
|
|
95
97
|
const stateHash = crypto.createHash('sha256').update(fs.readFileSync(value.statePath)).digest('hex');
|
|
@@ -97,7 +99,14 @@ function registration(root) {
|
|
|
97
99
|
const manifestPath = path.join(canonical(value.package.root), 'package.json');
|
|
98
100
|
if (canonical(value.package.manifestPath) !== canonical(manifestPath)) throw new Error('registration package manifest mismatch');
|
|
99
101
|
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
100
|
-
if (manifest.name !== PACKAGE_NAME || manifest.version !==
|
|
102
|
+
if (manifest.name !== PACKAGE_NAME || typeof manifest.version !== 'string') throw new Error('registration package mismatch');
|
|
103
|
+
if (value.version === LEGACY_REGISTRATION_VERSION && value.package.apiVersion === undefined) {
|
|
104
|
+
if (manifest.version !== value.package.version) throw new Error('registration package mismatch');
|
|
105
|
+
} else {
|
|
106
|
+
if (!Number.isSafeInteger(value.package.apiVersion) || value.package.apiVersion < 1) throw new Error('invalid package API version');
|
|
107
|
+
if (manifest.doompiApiVersion !== value.package.apiVersion) throw new Error('registration package API mismatch');
|
|
108
|
+
if (value.package.apiVersion !== API_VERSION) throw new Error('unsupported package API version');
|
|
109
|
+
}
|
|
101
110
|
const entries = manifest.pi?.extensions;
|
|
102
111
|
if (!Array.isArray(entries) || !entries.some((entry) => typeof entry === 'string' && canonical(path.resolve(value.package.root, entry)) === canonical(value.package.entry))) throw new Error('registration package entry mismatch');
|
|
103
112
|
return value;
|
|
@@ -133,11 +142,11 @@ function managedManifestVersion(directory) {
|
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
function managedManifest(directory) {
|
|
136
|
-
return managedManifestVersion(directory) ===
|
|
145
|
+
return managedManifestVersion(directory) === 3;
|
|
137
146
|
}
|
|
138
147
|
function upgradeableManagedManifest(directory) {
|
|
139
148
|
const version = managedManifestVersion(directory);
|
|
140
|
-
return version !== void 0 && version > 0 && version <=
|
|
149
|
+
return version !== void 0 && version > 0 && version <= 3;
|
|
141
150
|
}
|
|
142
151
|
/** Whether init's dispatcher package has a supported protocol and complete entry. */
|
|
143
152
|
function piExtensionDispatcherIsCurrent(piDirectory) {
|