@sellable/install
Version:
One-command installer for Sellable MCP in Claude Code, Codex, and Hermes
1,058 lines (1,021 loc) • 32.9 kB
JavaScript
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);
}
}