"use strict"; const fs = require("node:fs"); const path = require("node:path"); const packageMetadata = require("../../../../package.json"); const core = require(".."); const { applyHostConfigPatch, buildPatch, cleanupBackups, previewBackupCleanup, } = require("../adapters"); const { scanJson } = require("../mcp/strict-json"); const { createSkillTargetAdapter, ADAPTER_VERSION } = require("../target-adapter"); const { fsyncDirectorySync } = require("../durability"); const { inspectLayout, resolveDestination } = require("../transaction/safety"); const { validateInstance } = require("../schema-validator"); const EXIT = Object.freeze({ success: 0, invalid: 2, blocked: 3, recovery: 4, execution: 5 }); const PACKAGE_ROOT = path.resolve(__dirname, "../../../.."); function cliError(code, category, details = {}) { const error = new Error(code); error.code = code; error.category = category; error.details = details; return error; } function parseOptions(argv) { const positional = []; const options = {}; for (let index = 0; index < argv.length; index += 1) { const token = argv[index]; if (!token.startsWith("--")) { positional.push(token); continue; } const key = token.slice(2); if (["help", "override-managed-drift", "experimental-apply", "experimental-recovery", "preview-windows-output", "require-evidence"].includes(key)) { if (Object.hasOwn(options, key)) throw cliError("AAS_CLI_OPTION_DUPLICATE", "invalidInput", { option: key }); options[key] = true; continue; } const value = argv[index + 1]; if (!value || value.startsWith("--")) throw cliError("AAS_CLI_OPTION_VALUE_REQUIRED", "invalidInput", { option: key }); if (Object.hasOwn(options, key)) throw cliError("AAS_CLI_OPTION_DUPLICATE", "invalidInput", { option: key }); options[key] = value; index += 1; } return { positional, options }; } const COMMAND_OPTIONS = Object.freeze({ "catalog status": new Set(["cache-root", "version", "digest", "integrity", "help"]), "catalog update": new Set(["cache-root", "version", "help"]), "mcp configure": new Set(["host", "scope", "config", "cache-root", "version", "runtime-integrity", "runtime-closure-digest", "backup-dir", "retention", "approve", "help"]), "mcp backups cleanup": new Set(["config", "backup-dir", "keep", "approve", "help"]), "stack init": new Set(["goal", "catalog-digest", "cache-root", "host", "scope", "name", "out", "preview-windows-output", "help"]), "stack create": new Set(["selection", "evidence", "artifact-dir", "require-evidence", "catalog-digest", "cache-root", "out", "preview-windows-output", "help"]), "stack validate": new Set(["manifest", "help"]), "stack plan": new Set(["manifest", "target", "target-root", "cache-root", "runtime-version", "runtime-integrity", "out", "override-managed-drift", "preview-windows-output", "help"]), "stack apply": new Set(["plan", "target-root", "cache-root", "approve", "experimental-apply", "help"]), "stack doctor": new Set(["plan", "target-root", "cache-root", "help"]), "stack recover": new Set(["plan", "target-root", "cache-root", "id", "action", "approve", "experimental-recovery", "help"]), }); function invocationKey(positional) { if (positional[0] === "mcp" && positional[1] === "backups") return positional.slice(0, 3).join(" "); return positional.slice(0, 2).join(" "); } function validateInvocation(positional, options) { const key = invocationKey(positional); const allowed = COMMAND_OPTIONS[key]; const expectedLength = key === "mcp backups cleanup" ? 3 : 2; if (!allowed || positional.length !== expectedLength) { throw cliError("AAS_CLI_COMMAND_UNKNOWN", "invalidInput", { command: positional.join(" ") }); } const unknown = Object.keys(options).filter((option) => !allowed.has(option)).sort(); if (unknown.length) throw cliError("AAS_CLI_OPTION_UNKNOWN", "invalidInput", { options: unknown }); return key; } function requireOption(options, key) { if (!options[key]) throw cliError("AAS_CLI_OPTION_REQUIRED", "invalidInput", { option: key }); return options[key]; } function requireAbsoluteOption(options, key) { const value = requireOption(options, key); if (!path.isAbsolute(value) || value.includes("\0")) throw cliError("AAS_CLI_ABSOLUTE_PATH_REQUIRED", "invalidInput", { option: key }); return path.normalize(value); } function boundedInteger(value, field, minimum, maximum, fallback) { if (value === undefined) return fallback; if (!/^(0|[1-9]\d*)$/.test(value)) throw cliError("AAS_CLI_INTEGER_INVALID", "invalidInput", { option: field }); const parsed = Number(value); if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) throw cliError("AAS_CLI_INTEGER_INVALID", "invalidInput", { option: field }); return parsed; } function readJsonFile(filePath, maximumBytes = 4 * 1024 * 1024) { const absolute = path.resolve(filePath); const stat = fs.lstatSync(absolute); if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1 || stat.size > maximumBytes) { throw cliError("AAS_CLI_JSON_FILE_UNSAFE", "invalidInput", {}); } const text = fs.readFileSync(absolute, "utf8"); scanJson(text, 64); return JSON.parse(text); } function writeNewJson(filePath, value, { previewWindowsOutput = false } = {}) { const absolute = path.resolve(filePath); const parent = path.dirname(absolute); const stat = fs.lstatSync(parent); if (!stat.isDirectory() || stat.isSymbolicLink()) throw cliError("AAS_CLI_OUTPUT_PARENT_UNSAFE", "filesystem", {}); if (previewWindowsOutput && process.platform !== "win32") { throw cliError("AAS_CLI_PREVIEW_WINDOWS_OUTPUT_UNSUPPORTED", "invalidInput", {}); } const temporary = path.join(parent, `.aas-write-${process.pid}-${Date.now()}`); let descriptor; let linked = false; try { descriptor = fs.openSync(temporary, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o600); fs.writeFileSync(descriptor, `${core.canonicalJson(value)}\n`); fs.fsyncSync(descriptor); fs.closeSync(descriptor); descriptor = undefined; fs.linkSync(temporary, absolute); linked = true; fs.unlinkSync(temporary); try { fsyncDirectorySync(parent); return { outputDurability: "directorySynced", certificationStatus: "certifiable" }; } catch (error) { if (!previewWindowsOutput) throw error; return { outputDurability: "fileSyncedDirectoryUnverified", certificationStatus: "notCertified" }; } } catch (error) { if (descriptor !== undefined) fs.closeSync(descriptor); try { fs.unlinkSync(temporary); } catch {} if (linked) try { fs.unlinkSync(absolute); } catch {} if (error.code === "EEXIST") throw cliError("AAS_CLI_OUTPUT_EXISTS", "conflict", {}); throw error; } } function writeNewStackArtifactDirectory( directoryPath, { manifest, evidence }, { previewWindowsOutput = false, filesystem = fs, syncDirectory = fsyncDirectorySync, } = {}, ) { const absolute = path.resolve(directoryPath); const parent = path.dirname(absolute); const parentStat = filesystem.lstatSync(parent); if (!parentStat.isDirectory() || parentStat.isSymbolicLink()) { throw cliError("AAS_CLI_OUTPUT_PARENT_UNSAFE", "filesystem", {}); } if (previewWindowsOutput && process.platform !== "win32") { throw cliError("AAS_CLI_PREVIEW_WINDOWS_OUTPUT_UNSUPPORTED", "invalidInput", {}); } try { filesystem.lstatSync(absolute); throw cliError("AAS_CLI_OUTPUT_EXISTS", "conflict", {}); } catch (error) { if (error.code !== "ENOENT") throw error; } let stagingDirectory; let published = false; try { stagingDirectory = filesystem.mkdtempSync(path.join(parent, ".aas-artifact-")); filesystem.chmodSync(stagingDirectory, 0o700); for (const [name, value] of [ ["aas-stack.json", manifest], ["aas-selection-evidence.json", evidence], ]) { const outputPath = path.join(stagingDirectory, name); const descriptor = filesystem.openSync( outputPath, filesystem.constants.O_CREAT | filesystem.constants.O_EXCL | filesystem.constants.O_WRONLY, 0o600, ); try { filesystem.writeFileSync(descriptor, `${core.canonicalJson(value)}\n`); filesystem.fsyncSync(descriptor); } finally { filesystem.closeSync(descriptor); } } syncDirectory(stagingDirectory); try { filesystem.lstatSync(absolute); throw cliError("AAS_CLI_OUTPUT_EXISTS", "conflict", {}); } catch (error) { if (error.code !== "ENOENT") throw error; } // The staging directory is a sibling of the destination, so this single // rename publishes the manifest and evidence sidecar as one filesystem // object. Node has no portable atomic primitive for two independent paths. filesystem.renameSync(stagingDirectory, absolute); published = true; stagingDirectory = undefined; try { syncDirectory(parent); return { outputDurability: "directorySynced", certificationStatus: "certifiable" }; } catch (error) { if (!previewWindowsOutput) throw error; return { outputDurability: "fileSyncedDirectoryUnverified", certificationStatus: "notCertified" }; } } catch (error) { if (stagingDirectory) { try { filesystem.rmSync(stagingDirectory, { recursive: true, force: true }); } catch {} } if (published) { try { filesystem.rmSync(absolute, { recursive: true, force: true }); syncDirectory(parent); } catch {} } if (["EEXIST", "ENOTEMPTY"].includes(error.code)) { throw cliError("AAS_CLI_OUTPUT_EXISTS", "conflict", {}); } throw error; } } function windowsOutputDurabilityDetails(written, platform = process.platform) { if (platform !== "win32") return {}; return { ...(written.certificationStatus === "notCertified" ? { releaseProfile: "preview" } : {}), ...written, }; } function targetKey(target) { return `${target.host}:${target.scope}`; } function selectTarget(manifest, value) { const target = manifest.targets.find((candidate) => targetKey(candidate) === value); if (!target) throw cliError("AAS_CLI_TARGET_NOT_IN_MANIFEST", "invalidInput", { target: value }); return target; } function adapterFor(options, target, sourceRoot) { return createSkillTargetAdapter({ targetRoot: path.resolve(requireOption(options, "target-root")), sourceRoot, host: target.host, scope: target.scope, }); } async function catalogFor(options, expectedDigest) { const requestedDigest = expectedDigest || options["catalog-digest"]; if (!requestedDigest) return core.loadBundledCatalog({ root: PACKAGE_ROOT }); const cacheRoot = options["cache-root"] === undefined ? undefined : requireAbsoluteOption(options, "cache-root"); const resolver = core.cache.createVerifiedCatalogResolver({ cacheRoot, bundledRoot: PACKAGE_ROOT }); const catalog = await resolver(requestedDigest); if (!catalog) throw cliError("AAS_CACHE_CATALOG_NOT_VERIFIED", "integrity", { digest: requestedDigest }); return catalog; } async function verifiedRuntimeFor(options, expected, dependencies = {}) { if (typeof dependencies.resolveVerifiedRuntime === "function") return dependencies.resolveVerifiedRuntime({ options, expected }); const cacheRoot = requireAbsoluteOption(options, "cache-root"); const version = expected?.version || requireOption(options, "runtime-version"); const integrity = expected?.integrity || requireOption(options, "runtime-integrity"); const status = await core.cache.runtimeStatus({ cacheRoot, packageVersion: version, integrity, ...(expected?.closureDigest ? { closureDigest: expected.closureDigest } : {}), }); if (status.status !== "verified") throw cliError("AAS_RUNTIME_NOT_VERIFIED", "integrity", { status: status.status }); return { identity: status.runtimeIdentity, sourceRoot: path.join(status.targetPath, "package") }; } function buildOperations({ manifest, target, adapter, allowManagedDrift }) { const layout = inspectLayout(adapter, target); const state = core.transaction.readManagedState(layout.stateFile); const installedState = state ? { digest: state.stateDigest, entries: core.transaction.publicEntries(state.entries) } : { digest: core.transaction.digestManagedEntries([]), entries: [] }; const managed = new Map((state ? state.entries : []).map((entry) => [entry.skillId, entry])); const desired = new Set(manifest.skills.map((skill) => skill.id)); const operations = []; const overrides = []; const next = new Map(managed); for (const skill of manifest.skills) { const source = adapter.resolveSourceTree({ skillId: skill.id }); const sourceTreeDigest = core.transaction.treeDigest(source); const destination = resolveDestination(layout, skill.id); const current = managed.get(skill.id); if (!current) { if (fs.existsSync(destination)) throw cliError("AAS_TRANSACTION_UNMANAGED_COLLISION", "conflict", { skillId: skill.id }); operations.push({ kind: "install", skillId: skill.id, sourceTreeDigest, expectedTreeDigest: null, resultTreeDigest: sourceTreeDigest, backupRequired: false }); next.set(skill.id, { skillId: skill.id, treeDigest: sourceTreeDigest, catalogIntegrity: manifest.catalog.integrity }); continue; } if (!fs.existsSync(destination)) throw cliError("AAS_TRANSACTION_MANAGED_ENTRY_MISSING", "drift", { skillId: skill.id }); const actual = core.transaction.treeDigest(destination); if (actual !== current.treeDigest) { if (!allowManagedDrift) throw cliError("AAS_TRANSACTION_MANAGED_DRIFT", "drift", { skillId: skill.id }); overrides.push({ kind: "managedDrift", skillId: skill.id, reasonCodes: ["AAS_PLAN_MANAGED_DRIFT_APPROVED"] }); } if (actual !== sourceTreeDigest) { operations.push({ kind: "replaceManaged", skillId: skill.id, sourceTreeDigest, expectedTreeDigest: actual, resultTreeDigest: sourceTreeDigest, backupRequired: true }); next.set(skill.id, { skillId: skill.id, treeDigest: sourceTreeDigest, catalogIntegrity: manifest.catalog.integrity }); } } for (const [skillId, current] of managed) { if (desired.has(skillId)) continue; const destination = resolveDestination(layout, skillId); if (!fs.existsSync(destination)) throw cliError("AAS_TRANSACTION_MANAGED_ENTRY_MISSING", "drift", { skillId }); const actual = core.transaction.treeDigest(destination); if (actual !== current.treeDigest) { if (!allowManagedDrift) throw cliError("AAS_TRANSACTION_MANAGED_DRIFT", "drift", { skillId }); overrides.push({ kind: "managedDrift", skillId, reasonCodes: ["AAS_PLAN_MANAGED_DRIFT_APPROVED"] }); } operations.push({ kind: "removeManaged", skillId, sourceTreeDigest: null, expectedTreeDigest: actual, resultTreeDigest: null, backupRequired: true }); next.delete(skillId); } return { layout, state, installedState, operations, overrides, nextEntries: [...next.values()] }; } async function stackInit(options) { const catalog = await catalogFor(options); const host = options.host || "codex"; const scope = options.scope || "project"; const manifest = { schemaVersion: 2, name: options.name || "aas-stack", catalog: { package: catalog.package, version: catalog.version, integrity: catalog.digest }, targets: [{ host, scope }], profile: { goals: [requireOption(options, "goal")], languages: [], frameworks: [], constraints: [] }, skills: [], }; const validation = core.stack.validateManifest(manifest); if (!validation.ok) throw cliError(validation.code, validation.category, validation.details); const output = options.out || "aas-stack.json"; const written = writeNewJson(output, manifest, { previewWindowsOutput: options["preview-windows-output"] === true }); return { ok: true, status: "initialized", path: path.resolve(output), manifestDigest: validation.manifestDigest, ...windowsOutputDurabilityDetails(written), }; } async function stackCreate(options) { const catalog = await catalogFor(options); const composed = core.composeStack(catalog, readJsonFile(requireOption(options, "selection"))); const evidencePath = options.evidence; const artifactDirectory = options["artifact-dir"]; const auditRequested = evidencePath !== undefined || artifactDirectory !== undefined || options["require-evidence"] === true; if (auditRequested) { if (!evidencePath) throw cliError("AAS_CLI_EVIDENCE_REQUIRED", "invalidInput", { option: "evidence" }); if (!artifactDirectory) throw cliError("AAS_CLI_OPTION_REQUIRED", "invalidInput", { option: "artifact-dir" }); if (options.out !== undefined) { throw cliError("AAS_CLI_OPTION_CONFLICT", "invalidInput", { options: ["artifact-dir", "out"] }); } const evidence = readJsonFile(evidencePath); const validation = core.validateSelectionEvidence(evidence, { catalog, manifest: composed.manifest }); if (!validation.ok) throw cliError(validation.code, validation.category, validation.details); const written = writeNewStackArtifactDirectory(artifactDirectory, { manifest: composed.manifest, evidence, }, { previewWindowsOutput: options["preview-windows-output"] === true }); const absoluteArtifactDirectory = path.resolve(artifactDirectory); return { ok: true, status: "created", selectionSource: "agent", artifactDirectory: absoluteArtifactDirectory, path: path.join(absoluteArtifactDirectory, "aas-stack.json"), evidencePath: path.join(absoluteArtifactDirectory, "aas-selection-evidence.json"), selectedSkillIds: composed.manifest.skills.map((skill) => skill.id), manifestDigest: composed.manifestDigest, evidenceDigest: validation.evidenceDigest, ...windowsOutputDurabilityDetails(written), }; } const output = options.out || "aas-stack.json"; const written = writeNewJson(output, composed.manifest, { previewWindowsOutput: options["preview-windows-output"] === true }); return { ok: true, status: "created", selectionSource: "agent", path: path.resolve(output), selectedSkillIds: composed.manifest.skills.map((skill) => skill.id), manifestDigest: composed.manifestDigest, ...windowsOutputDurabilityDetails(written), }; } async function stackPlan(options, dependencies = {}) { const manifest = readJsonFile(requireOption(options, "manifest")); const validation = core.stack.validateManifest(manifest); if (!validation.ok) throw cliError(validation.code, validation.category, validation.details); const catalog = await catalogFor(options, manifest.catalog.integrity); if (manifest.catalog.package !== catalog.package || manifest.catalog.version !== catalog.version || manifest.catalog.integrity !== catalog.digest) { throw cliError("AAS_PLAN_CATALOG_MISMATCH", "integrity", {}); } const targetBase = selectTarget(manifest, requireOption(options, "target")); if (options["runtime-version"] !== undefined && options["runtime-version"] !== manifest.catalog.version) { throw cliError("AAS_PLAN_RUNTIME_CATALOG_MISMATCH", "integrity", {}); } const runtime = await verifiedRuntimeFor({ ...options, "runtime-version": manifest.catalog.version }, null, dependencies); if (runtime.identity.package !== manifest.catalog.package || runtime.identity.version !== manifest.catalog.version) { throw cliError("AAS_PLAN_RUNTIME_CATALOG_MISMATCH", "integrity", {}); } const adapter = adapterFor(options, targetBase, runtime.sourceRoot); const layout = inspectLayout(adapter, targetBase); const target = { ...targetBase, adapterVersion: ADAPTER_VERSION, identityDigest: adapter.computeTargetIdentity(layout, targetBase) }; const observed = buildOperations({ manifest, target, adapter, allowManagedDrift: options["override-managed-drift"] === true }); for (const desired of manifest.skills) { core.getSkill(catalog, desired.id); } const plan = core.stack.buildPlanEnvelope({ manifest, handshake: { protocolVersion: core.protocolVersion, coreVersion: core.coreVersion, catalogSchemaVersion: core.catalogSchemaVersion, }, catalog: manifest.catalog, runtime: runtime.identity, target, installedState: observed.installedState, operations: observed.operations, overrides: observed.overrides, stateCommit: { previousDigest: observed.installedState.digest, nextDigest: core.transaction.digestManagedEntries(observed.nextEntries), position: "final", }, }); const written = writeNewJson(requireOption(options, "out"), plan, { previewWindowsOutput: options["preview-windows-output"] === true }); return { ok: true, status: "planned", planDigest: plan.digest, operationCount: plan.payload.operations.length, out: path.resolve(options.out), ...windowsOutputDurabilityDetails(written), }; } function help() { return { ok: true, status: "help", commands: [ "catalog status [--cache-root --version --digest --integrity ]", "catalog update --cache-root --version ", "mcp configure --host codex|claude --scope user|project --config --cache-root [--version ] [--runtime-integrity --runtime-closure-digest ] [--backup-dir ] [--approve ]", "mcp backups cleanup --config --backup-dir --keep [--approve ]", "stack init --goal [--catalog-digest --cache-root ] [--preview-windows-output]", "stack create --selection --out [--catalog-digest --cache-root ]", "stack create --selection --evidence --artifact-dir --require-evidence [--catalog-digest --cache-root ]", "stack validate --manifest ", "stack plan --manifest --target --target-root --cache-root --runtime-integrity --out [--preview-windows-output]", "stack apply --experimental-apply --plan --target-root --cache-root --approve (EXPERIMENTAL; NOT CERTIFIED)", "stack doctor --plan --target-root --cache-root ", "stack recover --experimental-recovery --plan --target-root --cache-root --id --action rollback|cleanup [--approve ] (EXPERIMENTAL; NOT CERTIFIED)", ], }; } function mcpServer({ host, cacheRoot, version, integrity }) { const command = process.execPath; const args = [core.cache.runtimeMcpPath({ cacheRoot, packageVersion: version, integrity }), "--cache-root", cacheRoot]; return host === "codex" ? { command, args, enabled: true } : { command, args, env: {} }; } function configApprovalDigest({ host, scope, configPath, cacheRoot, release, patch }) { const runtime = { package: release.package, version: release.version, integrity: release.integrity }; if (release.closureDigest) runtime.closureDigest = release.closureDigest; return core.sha256(core.canonicalJson({ schemaVersion: 1, action: "mcp.configure", host, scope, configPathDigest: core.sha256(configPath), cacheRootDigest: core.sha256(cacheRoot), runtime, config: { exists: patch.exists, currentDigest: patch.currentDigest, nextDigest: patch.nextDigest }, })); } function explicitCachedRuntime(options) { const integrity = options["runtime-integrity"]; const closureDigest = options["runtime-closure-digest"]; if ((integrity === undefined) !== (closureDigest === undefined)) { throw cliError("AAS_CLI_RUNTIME_IDENTITY_INCOMPLETE", "invalidInput", { requiredOptions: ["runtime-integrity", "runtime-closure-digest"], }); } if (integrity === undefined) return null; try { core.cache.parseNpmIntegrity(integrity); } catch { throw cliError("AAS_CLI_RUNTIME_INTEGRITY_INVALID", "invalidInput", { option: "runtime-integrity" }); } if (!/^sha256-[0-9a-f]{64}$/.test(closureDigest)) { throw cliError("AAS_CLI_RUNTIME_CLOSURE_DIGEST_INVALID", "invalidInput", { option: "runtime-closure-digest" }); } return { integrity, closureDigest }; } async function verifiedCachedRuntime({ cacheRoot, version, integrity, closureDigest }) { const status = await core.cache.runtimeStatus({ cacheRoot, packageVersion: version, integrity, closureDigest, }); if (status.status !== "verified") { throw cliError("AAS_RUNTIME_NOT_VERIFIED", "integrity", { status: status.status }); } return status; } async function mcpConfigure(options, dependencies = {}) { const host = requireOption(options, "host"); const scope = requireOption(options, "scope"); if (!new Set(["codex", "claude"]).has(host)) throw cliError("AAS_ADAPTER_HOST_UNSUPPORTED", "invalidInput", { host }); if (!new Set(["user", "project"]).has(scope)) throw cliError("AAS_ADAPTER_SCOPE_INVALID", "invalidInput", { scope }); const configPath = requireAbsoluteOption(options, "config"); const cacheRoot = requireAbsoluteOption(options, "cache-root"); const backupDirectory = options["backup-dir"] === undefined ? undefined : requireAbsoluteOption(options, "backup-dir"); const retention = boundedInteger(options.retention, "retention", 1, 100, 5); const version = options.version || packageMetadata.version; const fetcher = dependencies.fetcher; const cachedIdentity = explicitCachedRuntime(options); const cached = cachedIdentity ? await verifiedCachedRuntime({ cacheRoot, version, ...cachedIdentity }) : null; const release = cached ? cached.runtimeIdentity : await core.cache.inspectRuntimeRelease({ version, ...(fetcher ? { fetcher } : {}) }); const patch = await buildPatch({ host, scope, configPath, server: mcpServer({ host, cacheRoot, version: release.version, integrity: release.integrity }) }); const approvalDigest = configApprovalDigest({ host, scope, configPath, cacheRoot, release, patch }); const preview = { ok: true, status: "approvalRequired", action: "mcp.configure", approvalDigest, runtime: { package: release.package, version: release.version, integrity: release.integrity, ...(release.closureDigest ? { closureDigest: release.closureDigest } : {}), }, config: { host, scope, exists: patch.exists, status: patch.status, currentDigest: patch.currentDigest, nextDigest: patch.nextDigest, redactedDiff: patch.redactedDiff, }, remediation: { action: "mcp.configure", args: { approvalDigest } }, }; if (!options.approve) return preview; if (options.approve !== approvalDigest) throw cliError("AAS_ADAPTER_APPROVAL_MISMATCH", "approval", {}); const installed = cachedIdentity ? await verifiedCachedRuntime({ cacheRoot, version: release.version, ...cachedIdentity }) : await core.cache.installRuntimeFromRegistry({ cacheRoot, version: release.version, expectedIntegrity: release.integrity, ...(fetcher ? { fetcher } : {}), }); const applied = await applyHostConfigPatch({ patch, approved: true, backupDirectory, retention }); return { ok: true, status: applied.status === "alreadyConfigured" ? "alreadyConfigured" : "configured", runtime: installed.runtimeIdentity, runtimeCacheStatus: cachedIdentity ? "verified" : installed.status, config: { host, scope, digest: applied.configDigest, backupCreated: Boolean(applied.backup) }, }; } async function mcpBackupCleanup(options) { const configPath = requireAbsoluteOption(options, "config"); const backupDirectory = requireAbsoluteOption(options, "backup-dir"); const keep = boundedInteger(requireOption(options, "keep"), "keep", 0, 100); const preview = await previewBackupCleanup({ backupDirectory, configPath, keep }); if (!options.approve) { return { ok: true, ...preview, action: "mcp.backups.cleanup", remediation: { action: "mcp.backups.cleanup", args: { approvalDigest: preview.approvalDigest } } }; } if (options.approve !== preview.approvalDigest) throw cliError("AAS_ADAPTER_APPROVAL_MISMATCH", "approval", {}); const result = await cleanupBackups({ backupDirectory, configPath, keep, approvalDigest: options.approve }); return { ok: true, status: result.status, retained: result.retained, removedCount: result.removed.length }; } async function execute(argv, dependencies = {}) { const { positional, options } = parseOptions(argv); if (positional.length === 0 || (positional.length === 1 && positional[0] === "help")) return help(); validateInvocation(positional, options); if (options.help) return help(); const [root, command] = positional; if (root === "catalog" && command === "status") { const catalog = core.loadBundledCatalog(); const result = { ok: true, status: "complete", bundled: { package: catalog.package, version: catalog.version, digest: catalog.digest } }; if (options["cache-root"] || options.version || options.digest) { result.cached = await core.cache.catalogStatus({ cacheRoot: requireAbsoluteOption(options, "cache-root"), packageVersion: requireOption(options, "version"), catalogDigest: requireOption(options, "digest"), ...(options.integrity ? { integrity: options.integrity } : {}), }); } return result; } if (root === "catalog" && command === "update") { return core.cache.updateCatalogFromRegistry({ cacheRoot: requireAbsoluteOption(options, "cache-root"), version: requireOption(options, "version"), }); } if (root === "mcp" && command === "configure") return mcpConfigure(options, dependencies); if (root === "mcp" && command === "backups" && positional[2] === "cleanup") return mcpBackupCleanup(options); if (root !== "stack") throw cliError("AAS_CLI_COMMAND_UNKNOWN", "invalidInput", { command: root }); if (command === "init") return stackInit(options); if (command === "create") return stackCreate(options); if (command === "validate") { const validation = core.stack.validateManifest(readJsonFile(requireOption(options, "manifest"))); if (!validation.ok) throw cliError(validation.code, validation.category, validation.details); return validation; } if (command === "plan") return stackPlan(options, dependencies); if (["apply", "doctor", "recover"].includes(command)) { const plan = readJsonFile(requireOption(options, "plan")); core.stack.validatePlanEnvelope(plan); const target = plan.payload?.target; if (!target) throw cliError("AAS_TRANSACTION_PLAN_INVALID", "integrity", {}); if (target.adapterVersion !== ADAPTER_VERSION) throw cliError("AAS_PLAN_ADAPTER_VERSION_INCOMPATIBLE", "incompatibleVersion", {}); if (command === "apply" && options["experimental-apply"] !== true) { throw cliError("AAS_STACK_APPLY_EXPERIMENTAL_DISABLED", "policy", { releaseProfile: "preview", certificationStatus: "notCertified", requiredOption: "experimental-apply", }); } if (command === "recover" && options["experimental-recovery"] !== true) { throw cliError("AAS_STACK_RECOVERY_EXPERIMENTAL_DISABLED", "policy", { releaseProfile: "preview", certificationStatus: "notCertified", requiredOption: "experimental-recovery", }); } const runtime = await verifiedRuntimeFor(options, plan.payload.runtime, dependencies); if (core.canonicalJson(runtime.identity) !== core.canonicalJson(plan.payload.runtime)) { throw cliError("AAS_TRANSACTION_RUNTIME_IDENTITY_MISMATCH", "integrity", {}); } const adapter = adapterFor(options, target, runtime.sourceRoot); if (command === "apply") { const result = core.transaction.applyPlan({ plan, adapter, approvalDigest: requireOption(options, "approve") }); return { ...result, releaseProfile: "preview", certificationStatus: "experimental" }; } if (command === "doctor") return core.transaction.doctor({ target, adapter }); const action = requireOption(options, "action"); const recoveryPlan = core.transaction.buildRecoveryPlan({ plan, adapter, recoveryId: requireOption(options, "id"), action }); if (!options.approve) return { ok: true, status: "approvalRequired", recoveryPlan, releaseProfile: "preview", certificationStatus: "experimental", }; const result = core.transaction.recover({ recoveryPlan, plan, adapter, approvalDigest: options.approve }); return { ...result, releaseProfile: "preview", certificationStatus: "experimental" }; } throw cliError("AAS_CLI_COMMAND_UNKNOWN", "invalidInput", { command }); } function exitCodeFor(error) { if (["invalidInput", "incompatibleVersion"].includes(error.category)) return EXIT.invalid; if (["integrity", "drift", "policy", "approval"].includes(error.category)) return EXIT.blocked; if (["conflict", "recovery"].includes(error.category)) return EXIT.recovery; return EXIT.execution; } async function main(argv = process.argv.slice(2), io = {}) { const stdout = io.stdout || process.stdout; const stderr = io.stderr || process.stderr; try { const result = await execute(argv); const envelope = { schemaVersion: 1, protocolVersion: core.protocolVersion, coreVersion: core.coreVersion, catalogSchemaVersion: core.catalogSchemaVersion, reasonCodes: [], unknown: [], details: {}, ...result, }; validateInstance("result-envelope.schema.json", envelope, "AAS_CLI_RESULT_SCHEMA_INVALID", "internal"); stdout.write(`${core.canonicalJson(envelope)}\n`); return EXIT.success; } catch (error) { const payload = { schemaVersion: 1, ok: false, status: "error", protocolVersion: core.protocolVersion, coreVersion: core.coreVersion, catalogSchemaVersion: core.catalogSchemaVersion, code: error.code || "AAS_CLI_EXECUTION_FAILED", category: error.category || "execution", details: error.details || {}, }; validateInstance("result-envelope.schema.json", payload, "AAS_CLI_ERROR_SCHEMA_INVALID", "internal"); stderr.write(`${core.canonicalJson(payload)}\n`); return exitCodeFor(payload); } } module.exports = { EXIT, adapterFor, buildOperations, configApprovalDigest, execute, main, mcpBackupCleanup, mcpConfigure, parseOptions, readJsonFile, stackPlan, windowsOutputDurabilityDetails, writeNewJson, writeNewStackArtifactDirectory, };