@agimon-ai/doompi 0.0.1-alpha.90 → 0.0.1-alpha.91
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/builders/cli/bootstrapLocator/index.cjs +18 -11
- package/dist/src/builders/cli/bootstrapLocator/index.cjs.map +1 -1
- package/dist/src/builders/cli/bootstrapLocator/index.d.mts +1 -0
- package/dist/src/builders/cli/bootstrapLocator/index.d.mts.map +1 -1
- package/dist/src/builders/cli/bootstrapLocator/index.mjs +19 -13
- package/dist/src/builders/cli/bootstrapLocator/index.mjs.map +1 -1
- package/dist/src/builders/server/mcpApp.cjs +120 -0
- package/dist/src/builders/server/mcpApp.cjs.map +1 -0
- package/dist/src/builders/server/mcpApp.mjs +116 -0
- package/dist/src/builders/server/mcpApp.mjs.map +1 -0
- package/dist/src/builders/server/mcpBundle.cjs +25 -1
- package/dist/src/builders/server/mcpBundle.cjs.map +1 -1
- package/dist/src/builders/server/mcpBundle.mjs +25 -1
- package/dist/src/builders/server/mcpBundle.mjs.map +1 -1
- package/dist/src/builders/server/runtime.cjs +70 -45
- package/dist/src/builders/server/runtime.cjs.map +1 -1
- package/dist/src/builders/server/runtime.mjs +68 -43
- package/dist/src/builders/server/runtime.mjs.map +1 -1
- package/dist/src/builders/server/sessionArtifact.cjs +11 -0
- package/dist/src/builders/server/sessionArtifact.cjs.map +1 -0
- package/dist/src/builders/server/sessionArtifact.mjs +11 -0
- package/dist/src/builders/server/sessionArtifact.mjs.map +1 -0
- package/dist/src/cli/commands/sync/index.cjs +3 -1
- package/dist/src/cli/commands/sync/index.cjs.map +1 -1
- package/dist/src/cli/commands/sync/index.mjs +3 -1
- package/dist/src/cli/commands/sync/index.mjs.map +1 -1
- package/dist/src/composition/syncDrift/index.cjs +5 -0
- package/dist/src/composition/syncDrift/index.cjs.map +1 -1
- package/dist/src/composition/syncDrift/index.d.mts +1 -1
- package/dist/src/composition/syncDrift/index.d.mts.map +1 -1
- package/dist/src/composition/syncDrift/index.mjs +5 -0
- package/dist/src/composition/syncDrift/index.mjs.map +1 -1
- package/dist/src/composition/syncState/index.cjs +12 -5
- package/dist/src/composition/syncState/index.cjs.map +1 -1
- package/dist/src/composition/syncState/index.d.mts +3 -0
- package/dist/src/composition/syncState/index.d.mts.map +1 -1
- package/dist/src/composition/syncState/index.mjs +13 -7
- package/dist/src/composition/syncState/index.mjs.map +1 -1
- package/dist/syncState.cjs +1 -0
- package/dist/syncState.d.mts +2 -2
- package/dist/syncState.mjs +2 -2
- package/package.json +24 -19
|
@@ -19,10 +19,6 @@ const SHA256 = /^[a-f0-9]{64}$/u;
|
|
|
19
19
|
function isRecord(value) {
|
|
20
20
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21
21
|
}
|
|
22
|
-
function stringRecord(value) {
|
|
23
|
-
if (!isRecord(value)) return {};
|
|
24
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
25
|
-
}
|
|
26
22
|
function canonicalPath(target) {
|
|
27
23
|
const absolute = node_path.default.resolve(target);
|
|
28
24
|
try {
|
|
@@ -54,19 +50,16 @@ function findSyncedRoot(cwd, homeDirectory = node_os.default.homedir()) {
|
|
|
54
50
|
const root = require_index.resolveDoomConfigurationRoot(cwd, homeDirectory);
|
|
55
51
|
return locationHasState(root, homeDirectory) ? canonicalPath(root) : void 0;
|
|
56
52
|
}
|
|
57
|
-
function
|
|
58
|
-
const registration = (0, _agimon_ai_doompi_core_syncRegistration.readSyncRegistration)(repoRoot, homeDirectory);
|
|
59
|
-
if (!registration) return void 0;
|
|
53
|
+
function readBootstrapStateFromRegistration(registration, homeDirectory = node_os.default.homedir()) {
|
|
60
54
|
const statePath = registration.statePath;
|
|
61
55
|
const generatedDirectory = registration.generationRoot;
|
|
62
|
-
const state = require_index$1.
|
|
63
|
-
if (!state) return void 0;
|
|
56
|
+
const state = require_index$1.readRegisteredSyncState(registration, homeDirectory);
|
|
64
57
|
const bootstrap = typeof state.bootstrap === "string" ? state.bootstrap : void 0;
|
|
65
58
|
if (bootstrap && !isInside(generatedDirectory, bootstrap)) throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);
|
|
66
59
|
let precompile;
|
|
67
60
|
if (state.precompile !== void 0) {
|
|
68
61
|
const value = state.precompile;
|
|
69
|
-
if (
|
|
62
|
+
if (value.version !== _agimon_ai_doompi_core_syncStateContract.PRECOMPILE_STATE_VERSION || value.strategy !== _agimon_ai_doompi_core_syncStateContract.BUNDLED_PRECOMPILE_STRATEGY || typeof value.bootstrapEntry !== "string" || typeof value.bootstrapManifest !== "string" || !isRecord(value.bundleManifests) || Object.values(value.bundleManifests).some((manifest) => typeof manifest !== "string")) throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);
|
|
70
63
|
precompile = {
|
|
71
64
|
version: value.version,
|
|
72
65
|
strategy: value.strategy,
|
|
@@ -79,10 +72,14 @@ function readBootstrapState(repoRoot, homeDirectory = node_os.default.homedir())
|
|
|
79
72
|
statePath,
|
|
80
73
|
generatedDirectory,
|
|
81
74
|
bootstrap,
|
|
82
|
-
bundles:
|
|
75
|
+
bundles: state.bundles ?? {},
|
|
83
76
|
precompile
|
|
84
77
|
};
|
|
85
78
|
}
|
|
79
|
+
function readBootstrapState(repoRoot, homeDirectory = node_os.default.homedir()) {
|
|
80
|
+
const registration = (0, _agimon_ai_doompi_core_syncRegistration.readSyncRegistration)(repoRoot, homeDirectory);
|
|
81
|
+
return registration ? readBootstrapStateFromRegistration(registration, homeDirectory) : void 0;
|
|
82
|
+
}
|
|
86
83
|
function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
87
84
|
if (!isInside(generatedDirectory, manifestPath)) return void 0;
|
|
88
85
|
try {
|
|
@@ -156,6 +153,15 @@ function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirect
|
|
|
156
153
|
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
157
154
|
};
|
|
158
155
|
}
|
|
156
|
+
/** Validates the bootstrap owned by one exact immutable registration. */
|
|
157
|
+
function readRegisteredBootstrapStatus(registration, expectedBootstrapEntry, homeDirectory = node_os.default.homedir()) {
|
|
158
|
+
const state = readBootstrapStateFromRegistration(registration, homeDirectory);
|
|
159
|
+
const expected = expectedBootstrapEntry ?? packagedDoomEntry();
|
|
160
|
+
return {
|
|
161
|
+
bootstrap: state.bootstrap,
|
|
162
|
+
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
163
|
+
};
|
|
164
|
+
}
|
|
159
165
|
/** Validates one selected composition without touching any inactive bundle inputs. */
|
|
160
166
|
function readBundleStatus(repoRoot, compositionFingerprint, homeDirectory = node_os.default.homedir()) {
|
|
161
167
|
const state = readBootstrapState(repoRoot, homeDirectory);
|
|
@@ -199,6 +205,7 @@ function readBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = n
|
|
|
199
205
|
exports.findSyncedRoot = findSyncedRoot;
|
|
200
206
|
exports.readBootstrapStatus = readBootstrapStatus;
|
|
201
207
|
exports.readBundleStatus = readBundleStatus;
|
|
208
|
+
exports.readRegisteredBootstrapStatus = readRegisteredBootstrapStatus;
|
|
202
209
|
exports.readStartupBootstrapStatus = readStartupBootstrapStatus;
|
|
203
210
|
|
|
204
211
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
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/syncRegistration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/syncStateContract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { EXTENSION_COMPILER_VERSION } from '../../../compiler/version';\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 parsed.version !== EXTENSION_COMPILER_VERSION ||\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
|
+
{"version":3,"file":"index.cjs","names":["path","fs","parseInputFingerprint","ownEntry","readSyncRegistration","os","resolveDoomConfigurationRoot","readRegisteredSyncState","PRECOMPILE_STATE_VERSION","BUNDLED_PRECOMPILE_STRATEGY","crypto","inputsAreFresh"],"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, type SyncRegistration } from '@agimon-ai/doompi-core/syncRegistration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/syncStateContract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { EXTENSION_COMPILER_VERSION } from '../../../compiler/version';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readRegisteredSyncState } 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 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 readBootstrapStateFromRegistration(\n registration: SyncRegistration,\n homeDirectory: string = os.homedir(),\n): BootstrapState {\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readRegisteredSyncState(registration, homeDirectory);\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 value.version !== PRECOMPILE_STATE_VERSION ||\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,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: state.bundles ?? {},\n precompile,\n };\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return registration ? readBootstrapStateFromRegistration(registration, homeDirectory) : undefined;\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 parsed.version !== EXTENSION_COMPILER_VERSION ||\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 the bootstrap owned by one exact immutable registration. */\nexport function readRegisteredBootstrapStatus(\n registration: SyncRegistration,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapStateFromRegistration(registration, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\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,3 +1,4 @@
|
|
|
1
|
+
import { SyncRegistration } from "@agimon-ai/doompi-core/syncRegistration";
|
|
1
2
|
//#region src/builders/cli/bootstrapLocator/index.d.ts
|
|
2
3
|
/** Finds the nearest configured repository with generated Doom sync state. */
|
|
3
4
|
export declare function findSyncedRoot(cwd: string, homeDirectory?: string): string | undefined;
|
|
@@ -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":";;;wBAsFgB,eAAe,aAAa"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { resolveDoomConfigurationRoot } from "../../../composition/repository/index.mjs";
|
|
2
2
|
import { ownEntry } from "../entryResolution.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { readRegisteredSyncState } from "../../../composition/syncState/index.mjs";
|
|
4
4
|
import { inputsAreFresh, parseInputFingerprint } from "../../../compiler/inputs.mjs";
|
|
5
5
|
import fs from "node:fs";
|
|
6
6
|
import path from "node:path";
|
|
@@ -14,10 +14,6 @@ const SHA256 = /^[a-f0-9]{64}$/u;
|
|
|
14
14
|
function isRecord(value) {
|
|
15
15
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16
16
|
}
|
|
17
|
-
function stringRecord(value) {
|
|
18
|
-
if (!isRecord(value)) return {};
|
|
19
|
-
return Object.fromEntries(Object.entries(value).filter((entry) => typeof entry[1] === "string"));
|
|
20
|
-
}
|
|
21
17
|
function canonicalPath(target) {
|
|
22
18
|
const absolute = path.resolve(target);
|
|
23
19
|
try {
|
|
@@ -49,19 +45,16 @@ function findSyncedRoot(cwd, homeDirectory = os.homedir()) {
|
|
|
49
45
|
const root = resolveDoomConfigurationRoot(cwd, homeDirectory);
|
|
50
46
|
return locationHasState(root, homeDirectory) ? canonicalPath(root) : void 0;
|
|
51
47
|
}
|
|
52
|
-
function
|
|
53
|
-
const registration = readSyncRegistration(repoRoot, homeDirectory);
|
|
54
|
-
if (!registration) return void 0;
|
|
48
|
+
function readBootstrapStateFromRegistration(registration, homeDirectory = os.homedir()) {
|
|
55
49
|
const statePath = registration.statePath;
|
|
56
50
|
const generatedDirectory = registration.generationRoot;
|
|
57
|
-
const state =
|
|
58
|
-
if (!state) return void 0;
|
|
51
|
+
const state = readRegisteredSyncState(registration, homeDirectory);
|
|
59
52
|
const bootstrap = typeof state.bootstrap === "string" ? state.bootstrap : void 0;
|
|
60
53
|
if (bootstrap && !isInside(generatedDirectory, bootstrap)) throw new Error(`Doom bootstrap must stay inside ${generatedDirectory}: ${bootstrap}`);
|
|
61
54
|
let precompile;
|
|
62
55
|
if (state.precompile !== void 0) {
|
|
63
56
|
const value = state.precompile;
|
|
64
|
-
if (
|
|
57
|
+
if (value.version !== PRECOMPILE_STATE_VERSION || value.strategy !== BUNDLED_PRECOMPILE_STRATEGY || typeof value.bootstrapEntry !== "string" || typeof value.bootstrapManifest !== "string" || !isRecord(value.bundleManifests) || Object.values(value.bundleManifests).some((manifest) => typeof manifest !== "string")) throw new Error(`Doom sync state at ${statePath} has an invalid precompile record`);
|
|
65
58
|
precompile = {
|
|
66
59
|
version: value.version,
|
|
67
60
|
strategy: value.strategy,
|
|
@@ -74,10 +67,14 @@ function readBootstrapState(repoRoot, homeDirectory = os.homedir()) {
|
|
|
74
67
|
statePath,
|
|
75
68
|
generatedDirectory,
|
|
76
69
|
bootstrap,
|
|
77
|
-
bundles:
|
|
70
|
+
bundles: state.bundles ?? {},
|
|
78
71
|
precompile
|
|
79
72
|
};
|
|
80
73
|
}
|
|
74
|
+
function readBootstrapState(repoRoot, homeDirectory = os.homedir()) {
|
|
75
|
+
const registration = readSyncRegistration(repoRoot, homeDirectory);
|
|
76
|
+
return registration ? readBootstrapStateFromRegistration(registration, homeDirectory) : void 0;
|
|
77
|
+
}
|
|
81
78
|
function readCompilerManifest(manifestPath, generatedDirectory) {
|
|
82
79
|
if (!isInside(generatedDirectory, manifestPath)) return void 0;
|
|
83
80
|
try {
|
|
@@ -151,6 +148,15 @@ function readStartupBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirect
|
|
|
151
148
|
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
152
149
|
};
|
|
153
150
|
}
|
|
151
|
+
/** Validates the bootstrap owned by one exact immutable registration. */
|
|
152
|
+
function readRegisteredBootstrapStatus(registration, expectedBootstrapEntry, homeDirectory = os.homedir()) {
|
|
153
|
+
const state = readBootstrapStateFromRegistration(registration, homeDirectory);
|
|
154
|
+
const expected = expectedBootstrapEntry ?? packagedDoomEntry();
|
|
155
|
+
return {
|
|
156
|
+
bootstrap: state.bootstrap,
|
|
157
|
+
fresh: usableBootstrapRecord(state, expected) !== void 0
|
|
158
|
+
};
|
|
159
|
+
}
|
|
154
160
|
/** Validates one selected composition without touching any inactive bundle inputs. */
|
|
155
161
|
function readBundleStatus(repoRoot, compositionFingerprint, homeDirectory = os.homedir()) {
|
|
156
162
|
const state = readBootstrapState(repoRoot, homeDirectory);
|
|
@@ -191,6 +197,6 @@ function readBootstrapStatus(repoRoot, expectedBootstrapEntry, homeDirectory = o
|
|
|
191
197
|
};
|
|
192
198
|
}
|
|
193
199
|
//#endregion
|
|
194
|
-
export { findSyncedRoot, readBootstrapStatus, readBundleStatus, readStartupBootstrapStatus };
|
|
200
|
+
export { findSyncedRoot, readBootstrapStatus, readBundleStatus, readRegisteredBootstrapStatus, readStartupBootstrapStatus };
|
|
195
201
|
|
|
196
202
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
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/syncRegistration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/syncStateContract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { EXTENSION_COMPILER_VERSION } from '../../../compiler/version';\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 parsed.version !== EXTENSION_COMPILER_VERSION ||\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
|
+
{"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, type SyncRegistration } from '@agimon-ai/doompi-core/syncRegistration';\nimport { BUNDLED_PRECOMPILE_STRATEGY, PRECOMPILE_STATE_VERSION } from '@agimon-ai/doompi-core/syncStateContract';\n\nimport { inputsAreFresh, parseInputFingerprint, type InputFingerprint } from '../../../compiler/inputs';\nimport { EXTENSION_COMPILER_VERSION } from '../../../compiler/version';\nimport { resolveDoomConfigurationRoot } from '../../../composition/repository';\nimport { readRegisteredSyncState } 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 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 readBootstrapStateFromRegistration(\n registration: SyncRegistration,\n homeDirectory: string = os.homedir(),\n): BootstrapState {\n const statePath = registration.statePath;\n const generatedDirectory = registration.generationRoot;\n const state = readRegisteredSyncState(registration, homeDirectory);\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 value.version !== PRECOMPILE_STATE_VERSION ||\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,\n };\n }\n\n return {\n statePath,\n generatedDirectory,\n bootstrap,\n bundles: state.bundles ?? {},\n precompile,\n };\n}\n\nfunction readBootstrapState(repoRoot: string, homeDirectory: string = os.homedir()): BootstrapState | undefined {\n const registration = readSyncRegistration(repoRoot, homeDirectory);\n return registration ? readBootstrapStateFromRegistration(registration, homeDirectory) : undefined;\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 parsed.version !== EXTENSION_COMPILER_VERSION ||\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 the bootstrap owned by one exact immutable registration. */\nexport function readRegisteredBootstrapStatus(\n registration: SyncRegistration,\n expectedBootstrapEntry?: string,\n homeDirectory: string = os.homedir(),\n): BootstrapStatus {\n const state = readBootstrapStateFromRegistration(registration, homeDirectory);\n const expected = expectedBootstrapEntry ?? packagedDoomEntry();\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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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
const require_runtime = require("../../../_virtual/_rolldown/runtime.cjs");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
node_fs = require_runtime.__toESM(node_fs, 1);
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
node_path = require_runtime.__toESM(node_path, 1);
|
|
6
|
+
let node_module = require("node:module");
|
|
7
|
+
let node_crypto = require("node:crypto");
|
|
8
|
+
node_crypto = require_runtime.__toESM(node_crypto, 1);
|
|
9
|
+
//#region src/builders/server/mcpApp.ts
|
|
10
|
+
/** Composes the installed packages' browser registries. No tool names or presentation policies live here. */
|
|
11
|
+
async function bundleMcpApp(sources, outputDirectory) {
|
|
12
|
+
if (sources.length === 0) return void 0;
|
|
13
|
+
const names = /* @__PURE__ */ new Set();
|
|
14
|
+
for (const source of sources) for (const name of source.widgets) {
|
|
15
|
+
if (names.has(name)) throw new Error(`Duplicate MCP widget '${name}'`);
|
|
16
|
+
names.add(name);
|
|
17
|
+
}
|
|
18
|
+
const runtimeRequire = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
|
|
19
|
+
const coreRoot = node_path.default.dirname(runtimeRequire.resolve("@agimon-ai/doompi-core/package.json"));
|
|
20
|
+
const componentsRoot = node_path.default.dirname(runtimeRequire.resolve("@agimon-ai/doompi-web-components/package.json"));
|
|
21
|
+
const componentsManifest = JSON.parse(node_fs.default.readFileSync(node_path.default.join(componentsRoot, "package.json"), "utf8"));
|
|
22
|
+
const appEntry = node_path.default.resolve(componentsRoot, componentsManifest.exports["./mcpWidget"].import);
|
|
23
|
+
const reactRequire = (0, node_module.createRequire)(node_path.default.join(coreRoot, "package.json"));
|
|
24
|
+
const directory = node_path.default.join(outputDirectory, "ui");
|
|
25
|
+
node_fs.default.mkdirSync(directory, { recursive: true });
|
|
26
|
+
const outputRoot = node_fs.default.realpathSync(outputDirectory);
|
|
27
|
+
const entry = node_path.default.join(directory, "entry.js");
|
|
28
|
+
const css = node_path.default.join(directory, "app.css");
|
|
29
|
+
const relative = (file) => file.split(node_path.default.sep).join("/");
|
|
30
|
+
node_fs.default.writeFileSync(entry, [
|
|
31
|
+
`import { mountMcpApp } from ${JSON.stringify(relative(appEntry))};`,
|
|
32
|
+
"import './app.css';",
|
|
33
|
+
...sources.map((source, index) => `import { widgets as widgets${index} } from ${JSON.stringify(relative(source.file))};`),
|
|
34
|
+
"const widgets = Object.create(null);",
|
|
35
|
+
...sources.flatMap((source, index) => source.widgets.map((name) => `widgets[${JSON.stringify(name)}] = widgets${index}[${JSON.stringify(name)}];`)),
|
|
36
|
+
"void mountMcpApp(widgets);",
|
|
37
|
+
""
|
|
38
|
+
].join("\n"));
|
|
39
|
+
node_fs.default.writeFileSync(css, [
|
|
40
|
+
`@import ${JSON.stringify(relative(runtimeRequire.resolve("tailwindcss/index.css")))};`,
|
|
41
|
+
`@import ${JSON.stringify(relative(node_path.default.join(componentsRoot, "styles/mcp.css")))};`,
|
|
42
|
+
`@source ${JSON.stringify(relative(node_path.default.join(componentsRoot, "dist")))};`,
|
|
43
|
+
...sources.map((source) => `@source ${JSON.stringify(relative(source.file))};`),
|
|
44
|
+
""
|
|
45
|
+
].join("\n"));
|
|
46
|
+
const inputs = /* @__PURE__ */ new Set([
|
|
47
|
+
appEntry,
|
|
48
|
+
node_path.default.join(coreRoot, "package.json"),
|
|
49
|
+
node_path.default.join(componentsRoot, "package.json")
|
|
50
|
+
]);
|
|
51
|
+
for (const root of [node_path.default.join(componentsRoot, "styles"), node_path.default.dirname(runtimeRequire.resolve("tailwindcss/package.json"))]) for (const file of node_fs.default.readdirSync(root, { recursive: true })) if (typeof file === "string" && file.endsWith(".css")) inputs.add(node_path.default.join(root, file));
|
|
52
|
+
const { build } = await import("vite");
|
|
53
|
+
const { default: tailwindcss } = await import("@tailwindcss/vite");
|
|
54
|
+
const built = await build({
|
|
55
|
+
configFile: false,
|
|
56
|
+
envDir: false,
|
|
57
|
+
root: directory,
|
|
58
|
+
logLevel: "warn",
|
|
59
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
60
|
+
plugins: [tailwindcss(), {
|
|
61
|
+
name: "doompi-mcp-inputs",
|
|
62
|
+
generateBundle() {
|
|
63
|
+
for (const id of this.getModuleIds()) {
|
|
64
|
+
const file = id.split("?")[0];
|
|
65
|
+
if (file.includes("\0") || !node_path.default.isAbsolute(file) || !node_fs.default.existsSync(file) || !node_fs.default.statSync(file).isFile()) continue;
|
|
66
|
+
const resolved = node_fs.default.realpathSync(file);
|
|
67
|
+
const withinOutput = node_path.default.relative(outputRoot, resolved);
|
|
68
|
+
if (withinOutput !== "" && !withinOutput.startsWith("..") && !node_path.default.isAbsolute(withinOutput)) continue;
|
|
69
|
+
inputs.add(resolved);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}],
|
|
73
|
+
resolve: { alias: [{
|
|
74
|
+
find: /^@agimon-ai\/doompi-web-components$/u,
|
|
75
|
+
replacement: appEntry
|
|
76
|
+
}, ...[
|
|
77
|
+
"react",
|
|
78
|
+
"react/jsx-runtime",
|
|
79
|
+
"react/jsx-dev-runtime",
|
|
80
|
+
"react-dom",
|
|
81
|
+
"react-dom/client"
|
|
82
|
+
].map((specifier) => ({
|
|
83
|
+
find: new RegExp(`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "u"),
|
|
84
|
+
replacement: reactRequire.resolve(specifier)
|
|
85
|
+
}))] },
|
|
86
|
+
build: {
|
|
87
|
+
write: false,
|
|
88
|
+
emptyOutDir: false,
|
|
89
|
+
sourcemap: false,
|
|
90
|
+
cssCodeSplit: false,
|
|
91
|
+
assetsInlineLimit: 0,
|
|
92
|
+
lib: {
|
|
93
|
+
entry,
|
|
94
|
+
formats: ["iife"],
|
|
95
|
+
name: "DoompiMcpApp",
|
|
96
|
+
fileName: "app",
|
|
97
|
+
cssFileName: "app"
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
const output = (Array.isArray(built) ? built : [built]).flatMap((bundle) => "output" in bundle ? bundle.output : []);
|
|
102
|
+
const scripts = output.filter((item) => item.type === "chunk");
|
|
103
|
+
const styles = output.filter((item) => item.type === "asset").filter((item) => item.fileName.endsWith(".css"));
|
|
104
|
+
if (scripts.length !== 1 || scripts[0].imports.some((file) => file !== scripts[0].fileName) || scripts[0].dynamicImports.length || output.length !== scripts.length + styles.length) throw new Error(`The composed MCP app must contain one self-contained script and inline CSS only: ${output.map((item) => item.fileName).join(", ")}; imports: ${scripts.flatMap((item) => [...item.imports, ...item.dynamicImports]).join(", ")}`);
|
|
105
|
+
const script = scripts[0].code.replace(/<\/script/giu, "<\\/script");
|
|
106
|
+
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Doompi tools</title><style>${styles.map((item) => typeof item.source === "string" ? item.source : Buffer.from(item.source).toString("utf8")).join("\n").replace(/<\/style/giu, "<\\/style")}</style></head><body><div id="root"></div><script>${script}<\/script></body></html>`;
|
|
107
|
+
node_fs.default.writeFileSync(node_path.default.join(directory, "index.html"), html);
|
|
108
|
+
return {
|
|
109
|
+
file: "./ui/index.html",
|
|
110
|
+
sha256: node_crypto.default.createHash("sha256").update(html).digest("hex"),
|
|
111
|
+
inputs: [...inputs].sort().map((file) => ({
|
|
112
|
+
path: file,
|
|
113
|
+
sha256: node_crypto.default.createHash("sha256").update(node_fs.default.readFileSync(file)).digest("hex")
|
|
114
|
+
}))
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
//#endregion
|
|
118
|
+
exports.bundleMcpApp = bundleMcpApp;
|
|
119
|
+
|
|
120
|
+
//# sourceMappingURL=mcpApp.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcpApp.cjs","names":["createRequire","path","fs","crypto"],"sources":["../../../../src/builders/server/mcpApp.ts"],"sourcesContent":["import crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { DoomMcpUiBundle } from '@agimon-ai/doompi-core/mcpFacet';\n\nexport interface McpAppSource {\n readonly file: string;\n readonly widgets: readonly string[];\n}\n\n/** Composes the installed packages' browser registries. No tool names or presentation policies live here. */\nexport async function bundleMcpApp(\n sources: readonly McpAppSource[],\n outputDirectory: string,\n): Promise<DoomMcpUiBundle | undefined> {\n if (sources.length === 0) return undefined;\n const names = new Set<string>();\n for (const source of sources) {\n for (const name of source.widgets) {\n if (names.has(name)) throw new Error(`Duplicate MCP widget '${name}'`);\n names.add(name);\n }\n }\n const runtimeRequire = createRequire(import.meta.url);\n const coreRoot = path.dirname(runtimeRequire.resolve('@agimon-ai/doompi-core/package.json'));\n const componentsRoot = path.dirname(runtimeRequire.resolve('@agimon-ai/doompi-web-components/package.json'));\n const componentsManifest = JSON.parse(fs.readFileSync(path.join(componentsRoot, 'package.json'), 'utf8')) as {\n exports: Record<string, { import: string }>;\n };\n const appEntry = path.resolve(componentsRoot, componentsManifest.exports['./mcpWidget']!.import);\n const reactRequire = createRequire(path.join(coreRoot, 'package.json'));\n const directory = path.join(outputDirectory, 'ui');\n fs.mkdirSync(directory, { recursive: true });\n const outputRoot = fs.realpathSync(outputDirectory);\n const entry = path.join(directory, 'entry.js');\n const css = path.join(directory, 'app.css');\n const relative = (file: string): string => file.split(path.sep).join('/');\n fs.writeFileSync(\n entry,\n [\n `import { mountMcpApp } from ${JSON.stringify(relative(appEntry))};`,\n \"import './app.css';\",\n ...sources.map(\n (source, index) => `import { widgets as widgets${index} } from ${JSON.stringify(relative(source.file))};`,\n ),\n 'const widgets = Object.create(null);',\n ...sources.flatMap((source, index) =>\n source.widgets.map((name) => `widgets[${JSON.stringify(name)}] = widgets${index}[${JSON.stringify(name)}];`),\n ),\n 'void mountMcpApp(widgets);',\n '',\n ].join('\\n'),\n );\n fs.writeFileSync(\n css,\n [\n `@import ${JSON.stringify(relative(runtimeRequire.resolve('tailwindcss/index.css')))};`,\n `@import ${JSON.stringify(relative(path.join(componentsRoot, 'styles/mcp.css')))};`,\n `@source ${JSON.stringify(relative(path.join(componentsRoot, 'dist')))};`,\n ...sources.map((source) => `@source ${JSON.stringify(relative(source.file))};`),\n '',\n ].join('\\n'),\n );\n const inputs = new Set<string>([\n appEntry,\n path.join(coreRoot, 'package.json'),\n path.join(componentsRoot, 'package.json'),\n ]);\n for (const root of [\n path.join(componentsRoot, 'styles'),\n path.dirname(runtimeRequire.resolve('tailwindcss/package.json')),\n ]) {\n for (const file of fs.readdirSync(root, { recursive: true })) {\n if (typeof file === 'string' && file.endsWith('.css')) inputs.add(path.join(root, file));\n }\n }\n const { build } = await import('vite');\n const { default: tailwindcss } = await import('@tailwindcss/vite');\n const built = await build({\n configFile: false,\n envDir: false,\n root: directory,\n logLevel: 'warn',\n // Library builds leave this React branch intact unless it is replaced explicitly.\n define: { 'process.env.NODE_ENV': JSON.stringify('production') },\n plugins: [\n tailwindcss(),\n {\n name: 'doompi-mcp-inputs',\n generateBundle() {\n for (const id of this.getModuleIds()) {\n const file = id.split('?')[0]!;\n if (file.includes('\\0') || !path.isAbsolute(file) || !fs.existsSync(file) || !fs.statSync(file).isFile())\n continue;\n const resolved = fs.realpathSync(file);\n const withinOutput = path.relative(outputRoot, resolved);\n if (withinOutput !== '' && !withinOutput.startsWith('..') && !path.isAbsolute(withinOutput)) continue;\n inputs.add(resolved);\n }\n },\n },\n ],\n resolve: {\n alias: [\n { find: /^@agimon-ai\\/doompi-web-components$/u, replacement: appEntry },\n ...['react', 'react/jsx-runtime', 'react/jsx-dev-runtime', 'react-dom', 'react-dom/client'].map(\n (specifier) => ({\n find: new RegExp(`^${specifier.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&')}$`, 'u'),\n replacement: reactRequire.resolve(specifier),\n }),\n ),\n ],\n },\n build: {\n write: false,\n emptyOutDir: false,\n sourcemap: false,\n cssCodeSplit: false,\n assetsInlineLimit: 0,\n lib: { entry, formats: ['iife'], name: 'DoompiMcpApp', fileName: 'app', cssFileName: 'app' },\n },\n });\n const bundles = Array.isArray(built) ? built : [built];\n const output = bundles.flatMap((bundle) => ('output' in bundle ? bundle.output : []));\n const scripts = output.filter((item) => item.type === 'chunk');\n const styles = output.filter((item) => item.type === 'asset').filter((item) => item.fileName.endsWith('.css'));\n if (\n scripts.length !== 1 ||\n scripts[0]!.imports.some((file) => file !== scripts[0]!.fileName) ||\n scripts[0]!.dynamicImports.length ||\n output.length !== scripts.length + styles.length\n )\n throw new Error(\n `The composed MCP app must contain one self-contained script and inline CSS only: ${output.map((item) => item.fileName).join(', ')}; imports: ${scripts.flatMap((item) => [...item.imports, ...item.dynamicImports]).join(', ')}`,\n );\n const script = scripts[0]!.code.replace(/<\\/script/giu, '<\\\\/script');\n const style = styles\n .map((item) => (typeof item.source === 'string' ? item.source : Buffer.from(item.source).toString('utf8')))\n .join('\\n')\n .replace(/<\\/style/giu, '<\\\\/style');\n const html = `<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"><title>Doompi tools</title><style>${style}</style></head><body><div id=\"root\"></div><script>${script}</script></body></html>`;\n fs.writeFileSync(path.join(directory, 'index.html'), html);\n // Only immutable HTML is served. The generated imports remain useful for diagnosing a sync build.\n return {\n file: './ui/index.html',\n sha256: crypto.createHash('sha256').update(html).digest('hex'),\n inputs: [...inputs]\n .sort()\n .map((file) => ({ path: file, sha256: crypto.createHash('sha256').update(fs.readFileSync(file)).digest('hex') })),\n };\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import crypto from "node:crypto";
|
|
5
|
+
//#region src/builders/server/mcpApp.ts
|
|
6
|
+
/** Composes the installed packages' browser registries. No tool names or presentation policies live here. */
|
|
7
|
+
async function bundleMcpApp(sources, outputDirectory) {
|
|
8
|
+
if (sources.length === 0) return void 0;
|
|
9
|
+
const names = /* @__PURE__ */ new Set();
|
|
10
|
+
for (const source of sources) for (const name of source.widgets) {
|
|
11
|
+
if (names.has(name)) throw new Error(`Duplicate MCP widget '${name}'`);
|
|
12
|
+
names.add(name);
|
|
13
|
+
}
|
|
14
|
+
const runtimeRequire = createRequire(import.meta.url);
|
|
15
|
+
const coreRoot = path.dirname(runtimeRequire.resolve("@agimon-ai/doompi-core/package.json"));
|
|
16
|
+
const componentsRoot = path.dirname(runtimeRequire.resolve("@agimon-ai/doompi-web-components/package.json"));
|
|
17
|
+
const componentsManifest = JSON.parse(fs.readFileSync(path.join(componentsRoot, "package.json"), "utf8"));
|
|
18
|
+
const appEntry = path.resolve(componentsRoot, componentsManifest.exports["./mcpWidget"].import);
|
|
19
|
+
const reactRequire = createRequire(path.join(coreRoot, "package.json"));
|
|
20
|
+
const directory = path.join(outputDirectory, "ui");
|
|
21
|
+
fs.mkdirSync(directory, { recursive: true });
|
|
22
|
+
const outputRoot = fs.realpathSync(outputDirectory);
|
|
23
|
+
const entry = path.join(directory, "entry.js");
|
|
24
|
+
const css = path.join(directory, "app.css");
|
|
25
|
+
const relative = (file) => file.split(path.sep).join("/");
|
|
26
|
+
fs.writeFileSync(entry, [
|
|
27
|
+
`import { mountMcpApp } from ${JSON.stringify(relative(appEntry))};`,
|
|
28
|
+
"import './app.css';",
|
|
29
|
+
...sources.map((source, index) => `import { widgets as widgets${index} } from ${JSON.stringify(relative(source.file))};`),
|
|
30
|
+
"const widgets = Object.create(null);",
|
|
31
|
+
...sources.flatMap((source, index) => source.widgets.map((name) => `widgets[${JSON.stringify(name)}] = widgets${index}[${JSON.stringify(name)}];`)),
|
|
32
|
+
"void mountMcpApp(widgets);",
|
|
33
|
+
""
|
|
34
|
+
].join("\n"));
|
|
35
|
+
fs.writeFileSync(css, [
|
|
36
|
+
`@import ${JSON.stringify(relative(runtimeRequire.resolve("tailwindcss/index.css")))};`,
|
|
37
|
+
`@import ${JSON.stringify(relative(path.join(componentsRoot, "styles/mcp.css")))};`,
|
|
38
|
+
`@source ${JSON.stringify(relative(path.join(componentsRoot, "dist")))};`,
|
|
39
|
+
...sources.map((source) => `@source ${JSON.stringify(relative(source.file))};`),
|
|
40
|
+
""
|
|
41
|
+
].join("\n"));
|
|
42
|
+
const inputs = /* @__PURE__ */ new Set([
|
|
43
|
+
appEntry,
|
|
44
|
+
path.join(coreRoot, "package.json"),
|
|
45
|
+
path.join(componentsRoot, "package.json")
|
|
46
|
+
]);
|
|
47
|
+
for (const root of [path.join(componentsRoot, "styles"), path.dirname(runtimeRequire.resolve("tailwindcss/package.json"))]) for (const file of fs.readdirSync(root, { recursive: true })) if (typeof file === "string" && file.endsWith(".css")) inputs.add(path.join(root, file));
|
|
48
|
+
const { build } = await import("vite");
|
|
49
|
+
const { default: tailwindcss } = await import("@tailwindcss/vite");
|
|
50
|
+
const built = await build({
|
|
51
|
+
configFile: false,
|
|
52
|
+
envDir: false,
|
|
53
|
+
root: directory,
|
|
54
|
+
logLevel: "warn",
|
|
55
|
+
define: { "process.env.NODE_ENV": JSON.stringify("production") },
|
|
56
|
+
plugins: [tailwindcss(), {
|
|
57
|
+
name: "doompi-mcp-inputs",
|
|
58
|
+
generateBundle() {
|
|
59
|
+
for (const id of this.getModuleIds()) {
|
|
60
|
+
const file = id.split("?")[0];
|
|
61
|
+
if (file.includes("\0") || !path.isAbsolute(file) || !fs.existsSync(file) || !fs.statSync(file).isFile()) continue;
|
|
62
|
+
const resolved = fs.realpathSync(file);
|
|
63
|
+
const withinOutput = path.relative(outputRoot, resolved);
|
|
64
|
+
if (withinOutput !== "" && !withinOutput.startsWith("..") && !path.isAbsolute(withinOutput)) continue;
|
|
65
|
+
inputs.add(resolved);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}],
|
|
69
|
+
resolve: { alias: [{
|
|
70
|
+
find: /^@agimon-ai\/doompi-web-components$/u,
|
|
71
|
+
replacement: appEntry
|
|
72
|
+
}, ...[
|
|
73
|
+
"react",
|
|
74
|
+
"react/jsx-runtime",
|
|
75
|
+
"react/jsx-dev-runtime",
|
|
76
|
+
"react-dom",
|
|
77
|
+
"react-dom/client"
|
|
78
|
+
].map((specifier) => ({
|
|
79
|
+
find: new RegExp(`^${specifier.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "u"),
|
|
80
|
+
replacement: reactRequire.resolve(specifier)
|
|
81
|
+
}))] },
|
|
82
|
+
build: {
|
|
83
|
+
write: false,
|
|
84
|
+
emptyOutDir: false,
|
|
85
|
+
sourcemap: false,
|
|
86
|
+
cssCodeSplit: false,
|
|
87
|
+
assetsInlineLimit: 0,
|
|
88
|
+
lib: {
|
|
89
|
+
entry,
|
|
90
|
+
formats: ["iife"],
|
|
91
|
+
name: "DoompiMcpApp",
|
|
92
|
+
fileName: "app",
|
|
93
|
+
cssFileName: "app"
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const output = (Array.isArray(built) ? built : [built]).flatMap((bundle) => "output" in bundle ? bundle.output : []);
|
|
98
|
+
const scripts = output.filter((item) => item.type === "chunk");
|
|
99
|
+
const styles = output.filter((item) => item.type === "asset").filter((item) => item.fileName.endsWith(".css"));
|
|
100
|
+
if (scripts.length !== 1 || scripts[0].imports.some((file) => file !== scripts[0].fileName) || scripts[0].dynamicImports.length || output.length !== scripts.length + styles.length) throw new Error(`The composed MCP app must contain one self-contained script and inline CSS only: ${output.map((item) => item.fileName).join(", ")}; imports: ${scripts.flatMap((item) => [...item.imports, ...item.dynamicImports]).join(", ")}`);
|
|
101
|
+
const script = scripts[0].code.replace(/<\/script/giu, "<\\/script");
|
|
102
|
+
const html = `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><title>Doompi tools</title><style>${styles.map((item) => typeof item.source === "string" ? item.source : Buffer.from(item.source).toString("utf8")).join("\n").replace(/<\/style/giu, "<\\/style")}</style></head><body><div id="root"></div><script>${script}<\/script></body></html>`;
|
|
103
|
+
fs.writeFileSync(path.join(directory, "index.html"), html);
|
|
104
|
+
return {
|
|
105
|
+
file: "./ui/index.html",
|
|
106
|
+
sha256: crypto.createHash("sha256").update(html).digest("hex"),
|
|
107
|
+
inputs: [...inputs].sort().map((file) => ({
|
|
108
|
+
path: file,
|
|
109
|
+
sha256: crypto.createHash("sha256").update(fs.readFileSync(file)).digest("hex")
|
|
110
|
+
}))
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { bundleMcpApp };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=mcpApp.mjs.map
|