UNPKG

@sellable/install

Version:

One-command installer for Sellable MCP in Claude Code, Codex, and Hermes

1,058 lines (1,021 loc) • 32.9 kB
import { spawn } from "node:child_process"; import { createHash } from "node:crypto"; import { closeSync, existsSync, fsyncSync, lstatSync, openSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, writeFileSync, } from "node:fs"; import { basename, dirname, isAbsolute, join, relative, resolve, } from "node:path"; import { fileURLToPath } from "node:url"; import { deriveContainedProfileId } from "./profile-materializer.mjs"; export const PROVISIONING_ACTION = "PROVISION_HERMES_PROFILE"; export const PINNED_INSTALL_PACKAGE = "@sellable/install@0.1.804"; export const PINNED_MCP_PACKAGE = "@sellable/mcp@0.1.957"; const PINNED_INSTALL_VERSION = PINNED_INSTALL_PACKAGE.slice( PINNED_INSTALL_PACKAGE.lastIndexOf("@") + 1 ); export function deriveAgentProfileId(workspaceId, agentId, hostId) { return deriveContainedProfileId(workspaceId, agentId, hostId); } const INSTALLER_BIN = fileURLToPath( new URL("../../bin/sellable-install.mjs", import.meta.url) ); const INSTALLER_PACKAGE_JSON = fileURLToPath( new URL("../../package.json", import.meta.url) ); const REQUEST_KEYS = new Set([ "action", "workspaceId", "agentId", "hostId", "generation", "desiredRevision", "idempotencyKey", "slackHomeChannel", ]); const SAFE_ID = /^[A-Za-z0-9_-]{1,128}$/; const PRIVATE_REFERENCE = /^secret:\/\/[A-Za-z0-9/_-]+$/; function validateRequest(request) { if (!request || typeof request !== "object" || Array.isArray(request)) { return { ok: false, code: "invalid_request" }; } const unknown = Object.keys(request).find((key) => !REQUEST_KEYS.has(key)); if (unknown) return { ok: false, code: "unknown_field", field: unknown }; if (request.action !== PROVISIONING_ACTION) { return { ok: false, code: "unknown_action" }; } for (const key of [ "workspaceId", "agentId", "hostId", "desiredRevision", "idempotencyKey", ]) { if (typeof request[key] !== "string" || !SAFE_ID.test(request[key])) { return { ok: false, code: "invalid_field", field: key }; } } if (!Number.isSafeInteger(request.generation) || request.generation < 1) { return { ok: false, code: "invalid_field", field: "generation" }; } if (!/^C[A-Z0-9]{8,20}$/.test(request.slackHomeChannel)) { return { ok: false, code: "invalid_field", field: "slackHomeChannel" }; } return { ok: true }; } function isInside(root, target) { const rel = relative(root, target); return rel === "" || (!rel.startsWith("..") && !isAbsolute(rel)); } function existingRealDirectory(pathValue) { if (!isAbsolute(pathValue) || !existsSync(pathValue)) return null; const link = lstatSync(pathValue); if (link.isSymbolicLink() || !link.isDirectory()) return null; return realpathSync(pathValue); } function assertDirectConfinedPath(profilesRoot, target, expectedBaseName) { const configuredRoot = resolve(profilesRoot); const realRoot = existingRealDirectory(configuredRoot); if (!realRoot) return false; const resolved = resolve(target); if ( dirname(resolved) !== configuredRoot || basename(resolved) !== expectedBaseName || !isInside(configuredRoot, resolved) ) { return false; } if (existsSync(resolved)) { const link = lstatSync(resolved); if (link.isSymbolicLink()) return false; const real = realpathSync(resolved); if (!isInside(realRoot, real) || dirname(real) !== realRoot) { return false; } } return true; } function resolveServerPaths(request, deps) { try { const profileId = deriveAgentProfileId( request.workspaceId, request.agentId, request.hostId ); const configuredProfilesRoot = typeof deps?.profilesRoot === "string" ? resolve(deps.profilesRoot) : null; const profilesRoot = existingRealDirectory(configuredProfilesRoot); const runtimeHome = existingRealDirectory(deps?.runtimeHome); if (!profilesRoot || !runtimeHome) { return { ok: false, code: "invalid_server_configuration" }; } const configuredProfile = deps?.profileRoots?.[profileId]; if ( typeof configuredProfile !== "string" || resolve(configuredProfile) !== join(configuredProfilesRoot, profileId) || !assertDirectConfinedPath( configuredProfilesRoot, configuredProfile, profileId ) ) { return { ok: false, code: "path_confinement_failed" }; } const profileRoot = resolve(configuredProfile); const stagingBase = `${profileId}.staging-${request.idempotencyKey}`; const backupBase = `${profileId}.rollback-${request.idempotencyKey}`; const stagingRoot = join(configuredProfilesRoot, stagingBase); const backupRoot = join(configuredProfilesRoot, backupBase); if ( !assertDirectConfinedPath( configuredProfilesRoot, stagingRoot, stagingBase ) || !assertDirectConfinedPath(configuredProfilesRoot, backupRoot, backupBase) ) { return { ok: false, code: "path_confinement_failed" }; } const credentialRoot = existingRealDirectory(deps?.credentialRoot); const credentialFile = deps?.credentialFiles?.[profileId]; if ( !credentialRoot || typeof credentialFile !== "string" || !isAbsolute(credentialFile) || !existsSync(credentialFile) ) { return { ok: false, code: "credential_confinement_failed" }; } const credentialLink = lstatSync(credentialFile); const credentialReal = credentialLink.isSymbolicLink() ? null : realpathSync(credentialFile); if ( !credentialReal || !credentialLink.isFile() || !isInside(credentialRoot, credentialReal) || (credentialLink.mode & 0o077) !== 0 ) { return { ok: false, code: "credential_confinement_failed" }; } const serviceCredentialReference = deps?.serviceCredentialReferences?.[profileId]; if (!PRIVATE_REFERENCE.test(serviceCredentialReference ?? "")) { return { ok: false, code: "invalid_server_configuration" }; } const credentialKeyVersion = deps?.credentialKeyVersions?.[profileId]; if ( typeof credentialKeyVersion !== "string" || !SAFE_ID.test(credentialKeyVersion) ) { return { ok: false, code: "invalid_server_configuration" }; } if ( !Number.isSafeInteger(deps.runtimeUid) || deps.runtimeUid < 1 || !Number.isSafeInteger(deps.runtimeGid) || deps.runtimeGid < 1 ) { return { ok: false, code: "invalid_server_configuration" }; } return { ok: true, profileId, profilesRoot: configuredProfilesRoot, profileRoot, stagingRoot, backupRoot, credentialFile: resolve(credentialFile), serviceCredentialReference, credentialKeyVersion, runtimeHome, }; } catch { return { ok: false, code: "path_confinement_failed" }; } } export function detectPinnedInstallerCapabilities() { try { const packageJson = JSON.parse( readFileSync(INSTALLER_PACKAGE_JSON, "utf8") ); const source = readFileSync(INSTALLER_BIN, "utf8"); const installVersion = packageJson.version; const capabilities = { hermesProfileBootstrap: source.includes('rawArgs[0] === "hermes"') && source.includes('rawArgs[2] === "bootstrap"'), tokenFile: source.includes('arg === "--token-file"'), profileRoot: source.includes('arg === "--profile-root"'), workspaceLock: source.includes('arg === "--require-workspace-lock"'), jsonReceipt: source.includes('arg === "--json"'), }; if ( `${packageJson.name}@${installVersion}` !== PINNED_INSTALL_PACKAGE || Object.values(capabilities).some((value) => value !== true) ) { return { ok: false, code: "installer_capability_mismatch" }; } return { ok: true, installPackage: PINNED_INSTALL_PACKAGE, installVersion, mcpPackage: PINNED_MCP_PACKAGE, capabilities, }; } catch { return { ok: false, code: "installer_capability_mismatch" }; } } function hasExactKeys(value, keys) { return ( value && typeof value === "object" && !Array.isArray(value) && Object.keys(value).sort().join("\0") === [...keys].sort().join("\0") ); } function isStringArray(value) { return ( Array.isArray(value) && value.every((item) => typeof item === "string") ); } export function validateInstallerAttestation({ stdout, expected }) { const failed = { ok: false, code: "installer_attestation_failed" }; try { const parsed = JSON.parse(stdout); const stagingRoot = resolve(expected.stagingRoot); const expectedPaths = { profileRoot: stagingRoot, hermesConfigPath: join(stagingRoot, "config.yaml"), envPath: join(stagingRoot, ".env"), sellableConfigPath: join(stagingRoot, "sellable", "config.json"), sellableConfigsDir: join(stagingRoot, "sellable", "configs"), skillsRoot: join(stagingRoot, "skills", "sellable"), }; if ( !hasExactKeys(parsed, [ "ok", "command", "profile", "dryRun", "paths", "auth", "slack", "created", "updated", "preserved", "skipped", "warnings", ]) || parsed.ok !== true || parsed.command !== "hermes profile bootstrap" || parsed.profile !== expected.profileId || parsed.dryRun !== false || !hasExactKeys(parsed.paths, Object.keys(expectedPaths)) || Object.entries(expectedPaths).some( ([key, value]) => parsed.paths[key] !== value ) || !hasExactKeys(parsed.auth, [ "apiUrl", "activeWorkspaceId", "activeWorkspaceName", "workspaceLock", "tokenPresent", "tokenSource", "tokenFingerprint", ]) || parsed.auth.apiUrl !== "https://app.sellable.dev" || parsed.auth.activeWorkspaceId !== expected.workspaceId || ![null, undefined].includes(parsed.auth.activeWorkspaceName) || !hasExactKeys(parsed.auth.workspaceLock, [ "enabled", "workspaceId", "required", ]) || parsed.auth.workspaceLock.enabled !== true || parsed.auth.workspaceLock.workspaceId !== expected.workspaceId || parsed.auth.workspaceLock.required !== true || parsed.auth.tokenPresent !== true || parsed.auth.tokenSource !== "token-file" || !/^[a-f0-9]{16}$/.test(parsed.auth.tokenFingerprint) || !hasExactKeys(parsed.slack, [ "keys", "envKeys", "fingerprints", "scope", ]) || !Array.isArray(parsed.slack.keys) || parsed.slack.keys.length !== 0 || JSON.stringify(parsed.slack.envKeys) !== JSON.stringify(["SLACK_HOME_CHANNEL", "SLACK_ALLOWED_CHANNELS"]) || !hasExactKeys(parsed.slack.fingerprints, []) || !hasExactKeys(parsed.slack.scope, [ "homeChannel", "homeChannelName", "allowedChannels", "freeResponseChannels", "allowedUsersCount", "requireMention", ]) || parsed.slack.scope.homeChannel !== expected.slackHomeChannel || parsed.slack.scope.homeChannelName !== null || JSON.stringify(parsed.slack.scope.allowedChannels) !== JSON.stringify([expected.slackHomeChannel]) || !Array.isArray(parsed.slack.scope.freeResponseChannels) || parsed.slack.scope.freeResponseChannels.length !== 0 || parsed.slack.scope.allowedUsersCount !== 0 || parsed.slack.scope.requireMention !== null || !isStringArray(parsed.created) || !isStringArray(parsed.updated) || !isStringArray(parsed.preserved) || !isStringArray(parsed.skipped) || !isStringArray(parsed.warnings) ) { return failed; } const allowedMaterializedPaths = new Set( Object.values(expectedPaths).slice(1) ); if ( [...parsed.created, ...parsed.updated, ...parsed.preserved].some( (value) => !allowedMaterializedPaths.has(value) ) || /xox[bap]-|sat_[^\s"']+|authorization[_-]?code|access[_-]?token|refresh[_-]?token/i.test( stdout ) ) { return failed; } return { ok: true, attestation: { credentialFingerprint: parsed.auth.tokenFingerprint, tokenSource: "token-file", }, }; } catch { return failed; } } export function buildProvisioningInvocation(request, deps) { const valid = validateRequest(request); if (!valid.ok) return valid; const paths = resolveServerPaths(request, deps); if (!paths.ok) return paths; return { ok: true, invocation: { package: PINNED_INSTALL_PACKAGE, mcpPackage: PINNED_MCP_PACKAGE, command: process.execPath, args: [ INSTALLER_BIN, "hermes", "profile", "bootstrap", "--profile-root", paths.stagingRoot, "--profile", paths.profileId, "--workspace-id", request.workspaceId, "--token-file", paths.credentialFile, "--slack-home-channel", request.slackHomeChannel, "--api-url", "https://app.sellable.dev", "--server", "package", "--mcp-package", PINNED_MCP_PACKAGE, "--require-workspace-lock", "--json", ], env: { HOME: paths.runtimeHome, NODE_ENV: "production", PATH: deps.runtimeEnv?.PATH || "/usr/bin:/bin", }, shell: false, timeoutMs: 60_000, maxOutputBytes: 64 * 1024, uid: deps.runtimeUid, gid: deps.runtimeGid, profileRoot: paths.stagingRoot, }, paths, }; } export async function runBoundedChildProcess(invocation) { return new Promise((resolveResult) => { let stdout = Buffer.alloc(0); let stderr = Buffer.alloc(0); let outputBytes = 0; let timedOut = false; let outputLimitExceeded = false; let terminationReason = null; let settled = false; const finish = (exitCode, signal, spawnFailed = false) => { if (settled) return; settled = true; clearTimeout(timer); resolveResult({ exitCode, signal, stdout: stdout.toString("utf8"), stderr: stderr.toString("utf8"), outputBytes, timedOut, outputLimitExceeded, terminated: timedOut || outputLimitExceeded, terminationReason: terminationReason ?? (spawnFailed ? "spawn_failed" : null), }); }; let child; try { child = spawn(invocation.command, invocation.args, { env: invocation.env, shell: false, stdio: ["ignore", "pipe", "pipe"], ...(Number.isSafeInteger(invocation.uid) ? { uid: invocation.uid } : {}), ...(Number.isSafeInteger(invocation.gid) ? { gid: invocation.gid } : {}), }); } catch { resolveResult({ exitCode: null, signal: null, stdout: "", stderr: "", outputBytes: 0, timedOut: false, outputLimitExceeded: false, terminated: false, terminationReason: "spawn_failed", }); return; } const capture = (target, chunk) => { const remaining = Math.max(0, invocation.maxOutputBytes - outputBytes); const kept = chunk.subarray(0, remaining); if (target === "stdout") stdout = Buffer.concat([stdout, kept]); else stderr = Buffer.concat([stderr, kept]); outputBytes += kept.length; if (chunk.length > remaining && !outputLimitExceeded) { outputLimitExceeded = true; terminationReason = "output_limit"; child.kill("SIGKILL"); } }; child.stdout.on("data", (chunk) => capture("stdout", Buffer.from(chunk))); child.stderr.on("data", (chunk) => capture("stderr", Buffer.from(chunk))); child.once("error", () => finish(null, null, true)); child.once("close", (code, signal) => finish(code, signal)); const timer = setTimeout(() => { timedOut = true; terminationReason = "timeout"; child.kill("SIGKILL"); }, invocation.timeoutMs); timer.unref?.(); }); } function matchesReadback(observed, request) { const profileId = deriveAgentProfileId( request.workspaceId, request.agentId, request.hostId ); return ( observed?.profileId === profileId && observed?.workspaceId === request.workspaceId && observed?.agentId === request.agentId && observed?.generation === request.generation && observed?.revision === request.desiredRevision && observed?.idempotencyKey === request.idempotencyKey ); } function sha256(value) { return createHash("sha256").update(value).digest("hex"); } function stableSerialize(value) { if (Array.isArray(value)) { return `[${value.map((item) => stableSerialize(item)).join(",")}]`; } if (value && typeof value === "object") { return `{${Object.keys(value) .sort() .map((key) => `${JSON.stringify(key)}:${stableSerialize(value[key])}`) .join(",")}}`; } return JSON.stringify(value); } function hashProfileTree(root, { excludeManifest = false } = {}) { const digest = createHash("sha256"); const walk = (pathValue, rel) => { const link = lstatSync(pathValue); if (link.isSymbolicLink()) throw new Error("symlink in profile"); const mode = (link.mode & 0o777).toString(8).padStart(3, "0"); if (link.isDirectory()) { digest.update(`D\0${rel}\0${mode}\n`); for (const name of readdirSync(pathValue).sort()) { const childRel = rel ? `${rel}/${name}` : name; if (excludeManifest && childRel === "agent-profile.json") continue; walk(join(pathValue, name), childRel); } return; } if (!link.isFile()) throw new Error("unsupported profile entry"); digest.update(`F\0${rel}\0${mode}\0${link.size}\n`); digest.update(readFileSync(pathValue)); digest.update("\n"); }; walk(root, ""); return digest.digest("hex"); } function inspectProvisionedProfile({ profileRoot, request, serviceCredentialReference, credentialKeyVersion, }) { try { const manifestPath = join(profileRoot, "agent-profile.json"); const manifestLink = lstatSync(manifestPath); if ( manifestLink.isSymbolicLink() || !manifestLink.isFile() || (manifestLink.mode & 0o777) !== 0o600 ) { return { ok: false, code: "manifest_mode_invalid" }; } const manifestBytes = readFileSync(manifestPath); const manifest = JSON.parse(manifestBytes.toString("utf8")); if ( !hasExactKeys(manifest, [ "profileId", "workspaceId", "agentId", "generation", "revision", "idempotencyKey", "serviceCredentialReference", "installerPackage", "installerVersion", "mcpPackage", "credentialFingerprint", "credentialKeyVersion", "profileContentDigest", "producer", "subject", ]) || !matchesReadback(manifest, request) || manifest.serviceCredentialReference !== serviceCredentialReference || manifest.installerPackage !== PINNED_INSTALL_PACKAGE || manifest.installerVersion !== PINNED_INSTALL_VERSION || manifest.mcpPackage !== PINNED_MCP_PACKAGE || manifest.credentialKeyVersion !== credentialKeyVersion || !/^[a-f0-9]{16}$/.test(manifest.credentialFingerprint) || !/^[a-f0-9]{64}$/.test(manifest.profileContentDigest) || manifest.producer !== "sellable-agent-provisioning-adapter" || manifest.subject !== `agent-profile:${manifest.profileId}` ) { return { ok: false, code: "manifest_binding_invalid" }; } const authPath = join(profileRoot, "sellable", "config.json"); const authLink = lstatSync(authPath); if ( authLink.isSymbolicLink() || !authLink.isFile() || (authLink.mode & 0o777) !== 0o600 ) { return { ok: false, code: "auth_mode_invalid" }; } const envPath = join(profileRoot, ".env"); if (existsSync(envPath)) { const envLink = lstatSync(envPath); if ( envLink.isSymbolicLink() || !envLink.isFile() || (envLink.mode & 0o777) !== 0o600 ) { return { ok: false, code: "env_mode_invalid" }; } } const auth = JSON.parse(readFileSync(authPath, "utf8")); const fingerprint = typeof auth.token === "string" ? sha256(auth.token).slice(0, 16) : null; if ( typeof auth.token !== "string" || !auth.token.startsWith("sat_") || auth.activeWorkspaceId !== request.workspaceId || auth.workspaceId !== request.workspaceId || auth.bootstrap?.profile !== manifest.profileId || fingerprint !== manifest.credentialFingerprint ) { return { ok: false, code: "auth_binding_invalid" }; } const contentDigest = hashProfileTree(profileRoot, { excludeManifest: true, }); if (contentDigest !== manifest.profileContentDigest) { return { ok: false, code: "profile_content_digest_mismatch" }; } return { ok: true, observed: manifest, credential: { fingerprint, keyVersion: credentialKeyVersion }, digests: { current: hashProfileTree(profileRoot), manifest: sha256(manifestBytes), content: contentDigest, }, }; } catch { return { ok: false, code: "profile_inspection_failed" }; } } function safeRemoveDirect(profilesRoot, target) { const targetBase = basename(target); if (!assertDirectConfinedPath(profilesRoot, target, targetBase)) { throw new Error("confined removal rejected"); } if (existsSync(target)) rmSync(target, { recursive: true, force: true }); } function fsyncPath(pathValue) { const descriptor = openSync(pathValue, "r"); try { fsyncSync(descriptor); } finally { closeSync(descriptor); } } function fsyncTree(root) { const link = lstatSync(root); if (link.isSymbolicLink()) throw new Error("symlink in staged profile"); if (link.isDirectory()) { for (const name of readdirSync(root)) fsyncTree(join(root, name)); } fsyncPath(root); } async function rollbackPromotedProfile({ profilesRoot, profileRoot, backupRoot, }) { if (existsSync(profileRoot)) safeRemoveDirect(profilesRoot, profileRoot); if (existsSync(backupRoot)) renameSync(backupRoot, profileRoot); fsyncPath(profilesRoot); } export async function atomicPromoteProfile({ profilesRoot, profileRoot, stagingRoot, backupRoot, }) { const configuredProfilesRoot = resolve(profilesRoot); const realProfilesRoot = existingRealDirectory(configuredProfilesRoot); if (!realProfilesRoot) throw new Error("profile promotion confinement failed"); for (const target of [profileRoot, stagingRoot, backupRoot]) { if ( !assertDirectConfinedPath( configuredProfilesRoot, target, basename(target) ) ) { throw new Error("profile promotion confinement failed"); } } if (existsSync(stagingRoot)) fsyncTree(stagingRoot); if (existsSync(backupRoot)) safeRemoveDirect(configuredProfilesRoot, backupRoot); const hadPrevious = existsSync(profileRoot); if (hadPrevious) renameSync(profileRoot, backupRoot); try { renameSync(stagingRoot, profileRoot); fsyncPath(configuredProfilesRoot); return { hadPrevious }; } catch { if (existsSync(profileRoot)) safeRemoveDirect(configuredProfilesRoot, profileRoot); if (hadPrevious && existsSync(backupRoot)) renameSync(backupRoot, profileRoot); fsyncPath(configuredProfilesRoot); throw new Error("profile promotion failed; prior profile restored"); } } export async function promoteProfileWithReadback(input) { let promotion; try { promotion = await atomicPromoteProfile(input); } catch { return { ok: false, code: "promotion_failed" }; } try { const observed = JSON.parse( readFileSync(join(input.profileRoot, "agent-profile.json"), "utf8") ); if (!matchesReadback(observed, input.request)) { await rollbackPromotedProfile(input); return { ok: false, code: "readback_mismatch", rolledBack: true, rollback: { outcome: promotion.hadPrevious ? "RESTORED" : "REMOVED" }, }; } if (input.expectedInspection) { const inspection = inspectProvisionedProfile({ profileRoot: input.profileRoot, request: input.request, serviceCredentialReference: input.expectedInspection.serviceCredentialReference, credentialKeyVersion: input.expectedInspection.credentialKeyVersion, }); if ( !inspection.ok || inspection.digests.current !== input.expectedInspection.currentDigest ) { await rollbackPromotedProfile(input); return { ok: false, code: "readback_mismatch", rolledBack: true, rollback: { outcome: promotion.hadPrevious ? "RESTORED" : "REMOVED" }, }; } return { ok: true, observed, inspection }; } return { ok: true, observed }; } catch { await rollbackPromotedProfile(input); return { ok: false, code: "readback_failed", rolledBack: true, rollback: { outcome: promotion.hadPrevious ? "RESTORED" : "REMOVED" }, }; } } function finalizeReceipt(receipt) { return { ...receipt, receiptDigest: sha256(stableSerialize(receipt)) }; } function successReceipt({ request, stages, inspection, previousDigest, outputBytes, reused = false, }) { const observed = inspection.observed; return finalizeReceipt({ ok: true, action: PROVISIONING_ACTION, producer: "sellable-agent-provisioning-adapter", subject: `agent-profile:${observed.profileId}`, cli: { installPackage: PINNED_INSTALL_PACKAGE, installVersion: PINNED_INSTALL_VERSION, mcpPackage: PINNED_MCP_PACKAGE, command: "hermes profile bootstrap", }, stages, observed: { profileId: observed.profileId, workspaceId: observed.workspaceId, agentId: observed.agentId, generation: observed.generation, revision: observed.revision, }, credential: inspection.credential, digests: { previous: previousDigest, current: inspection.digests.current, readback: inspection.digests.current, manifest: inspection.digests.manifest, content: inspection.digests.content, }, rollback: { outcome: "NOT_REQUIRED" }, idempotencyKey: request.idempotencyKey, outputBytes, reused, }); } function failure(code, stages, extra = {}, request = null) { const safeIdentity = request && SAFE_ID.test(request.workspaceId ?? "") && SAFE_ID.test(request.agentId ?? "") ? deriveAgentProfileId( request.workspaceId, request.agentId, request.hostId ) : null; return finalizeReceipt({ ok: false, code, action: PROVISIONING_ACTION, producer: "sellable-agent-provisioning-adapter", subject: safeIdentity ? `agent-profile:${safeIdentity}` : "agent-profile:unbound", cli: { installPackage: PINNED_INSTALL_PACKAGE, installVersion: PINNED_INSTALL_VERSION, mcpPackage: PINNED_MCP_PACKAGE, command: "hermes profile bootstrap", }, stages, rollback: extra.rollback ?? { outcome: "NOT_APPLICABLE" }, ...(extra.terminated === true ? { terminated: true, terminationReason: extra.terminationReason, } : {}), }); } export async function runProvisioningTransaction(request, deps) { const built = buildProvisioningInvocation(request, deps); if (!built.ok) return { ...built, stages: [] }; const { paths, invocation } = built; const stages = []; const capability = detectPinnedInstallerCapabilities(); stages.push("capability"); if (!capability.ok) return failure(capability.code, stages, {}, request); let previousDigest = null; if (existsSync(paths.profileRoot)) { try { previousDigest = hashProfileTree(paths.profileRoot); } catch { previousDigest = null; } } if (existsSync(join(paths.profileRoot, "agent-profile.json"))) { const current = inspectProvisionedProfile({ profileRoot: paths.profileRoot, request, serviceCredentialReference: paths.serviceCredentialReference, credentialKeyVersion: paths.credentialKeyVersion, }); if (current.ok) { stages.push("readback"); return successReceipt({ request, stages, inspection: current, previousDigest: current.digests.current, outputBytes: 0, reused: true, }); } } try { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); safeRemoveDirect(paths.profilesRoot, paths.backupRoot); stages.push("stage"); const output = await runBoundedChildProcess(invocation); if (output.timedOut) { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return failure( "installer_timeout", stages, { terminated: output.terminated, terminationReason: output.terminationReason, }, request ); } if (output.outputLimitExceeded) { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return failure( "installer_output_limit", stages, { terminated: output.terminated, terminationReason: output.terminationReason, }, request ); } if (output.exitCode !== 0) { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return failure("installer_failed", stages, {}, request); } stages.push("attest"); const attestation = validateInstallerAttestation({ stdout: output.stdout, expected: { profileId: paths.profileId, stagingRoot: paths.stagingRoot, workspaceId: request.workspaceId, slackHomeChannel: request.slackHomeChannel, }, }); if (!attestation.ok) { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return failure(attestation.code, stages, {}, request); } const profileContentDigest = hashProfileTree(paths.stagingRoot, { excludeManifest: true, }); const manifest = { profileId: paths.profileId, workspaceId: request.workspaceId, agentId: request.agentId, generation: request.generation, revision: request.desiredRevision, idempotencyKey: request.idempotencyKey, serviceCredentialReference: paths.serviceCredentialReference, installerPackage: PINNED_INSTALL_PACKAGE, installerVersion: capability.installVersion, mcpPackage: PINNED_MCP_PACKAGE, credentialFingerprint: attestation.attestation.credentialFingerprint, credentialKeyVersion: paths.credentialKeyVersion, profileContentDigest, producer: "sellable-agent-provisioning-adapter", subject: `agent-profile:${paths.profileId}`, }; writeFileSync( join(paths.stagingRoot, "agent-profile.json"), `${JSON.stringify(manifest, null, 2)}\n`, { mode: 0o600 } ); stages.push("validate"); const staged = inspectProvisionedProfile({ profileRoot: paths.stagingRoot, request, serviceCredentialReference: paths.serviceCredentialReference, credentialKeyVersion: paths.credentialKeyVersion, }); if (!staged.ok) { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return failure("staged_validation_failed", stages, {}, request); } fsyncTree(paths.stagingRoot); stages.push("promote"); const promoted = await promoteProfileWithReadback({ profilesRoot: paths.profilesRoot, profileRoot: paths.profileRoot, stagingRoot: paths.stagingRoot, backupRoot: paths.backupRoot, request, expectedInspection: { serviceCredentialReference: paths.serviceCredentialReference, credentialKeyVersion: paths.credentialKeyVersion, currentDigest: staged.digests.current, }, }); stages.push("readback"); if (!promoted.ok) { if (promoted.rolledBack) stages.push("rollback"); return failure( promoted.code, stages, { rollback: promoted.rollback ?? { outcome: "NOT_APPLICABLE" } }, request ); } safeRemoveDirect(paths.profilesRoot, paths.backupRoot); safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); return successReceipt({ request, stages, inspection: promoted.inspection, previousDigest, outputBytes: output.outputBytes, }); } catch { try { safeRemoveDirect(paths.profilesRoot, paths.stagingRoot); } catch {} return failure("provisioning_failed", stages, {}, request); } }