// Shared fixtures. Every test works in its own temporary directory and // points MOSAIC_CONFIG there; nothing reads the real ~/.config. import { chmodSync, cpSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { dirname, join, resolve } from "node:path"; import { fileURLToPath } from "node:url"; export const REPO = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..", ".."); export const REPO_ROLES = join(REPO, "roles"); export const FAR_DATE = "2099-01-01"; export function tmp() { return mkdtempSync(join(tmpdir(), "mosaic-business-")); } export function writeJson(file, value, mode = 0o600) { mkdirSync(dirname(file), { recursive: true }); writeFileSync(file, typeof value === "string" ? value : `${JSON.stringify(value, null, 2)}\n`); chmodSync(file, mode); return file; } // A token file with a placeholder value; the package never reads it. export function tokenFile(dir, name) { mkdirSync(dir, { recursive: true, mode: 0o700 }); const file = join(dir, name); writeFileSync(file, "placeholder-not-a-token"); chmodSync(file, 0o600); return file; } // A copy of the shipped role files in a scratch roles directory. export function rolesCopy(root) { const dir = join(root, "roles"); cpSync(REPO_ROLES, dir, { recursive: true }); return dir; } // A complete valid business document for the shipped four roles, with // token files under `/secrets`, and a project root under ``. export function businessDoc(root, id = "acme") { const secrets = join(root, "secrets"); const ref = (role, service) => service === "gitea" ? { file: tokenFile(secrets, `${role}-gitea.token`), rotateBy: FAR_DATE } : { file: tokenFile(secrets, `${role}-vikunja.token`), expires: FAR_DATE }; const role = (definition, botId, extra = {}) => ({ definition, tracker: { bot: `bot-${id}-${definition}`, botId }, credentials: { gitea: ref(definition, "gitea"), vikunja: ref(definition, "vikunja") }, ...extra, }); const projectRoot = join(root, "project"); mkdirSync(projectRoot, { recursive: true }); return { businessVersion: 1, id, human: "jason", arbiters: { delivery: "pm", technical: "cto" }, projects: { stack: { root: projectRoot } }, vars: { "tracker.baseUrl": "http://127.0.0.1:3456", "gitea.baseUrl": "https://git.example", "tracker.pollSeconds": 60 }, tracker: { sync: { bot: `bot-${id}-sync`, botId: 9, credentials: { vikunja: ref("sync", "vikunja") } }, labels: {}, }, roles: { pm: role("pm", 3, { holder: "sage", vars: { harness: "pi", model: "claude-opus-5-5" } }), cto: role("cto", 4, { holder: "darkwing", vars: { harness: "claude-code" } }), coder: role("coder", 5, { vars: { harness: "pi", "limits.network": "none" } }), reviewer: role("reviewer", 6), }, launch: { by: "pm", instances: ["coder", "reviewer"], max: { opus: 2, sonnet: 4 } }, }; } // The system config file mosaic-config.mjs validates, under `root`. export function systemConfig(root) { const file = join(root, "config", "config.json"); writeJson(file, { configVersion: 1, environment: "development", dataRoot: join(root, "data"), execution: { backend: "docker", provider: "zai", model: "glm-5.3-flash", adapter: "mock" }, }); return file; } // The checked system layer, as systemVars() returns it, without spawning. export function systemFor(root) { return { environment: "development", dataRoot: join(root, "data"), "execution.backend": "docker", "execution.provider": "zai", "execution.model": "glm-5.3-flash", "execution.adapter": "mock", }; }