UNPKG

trellis

Version:

Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications

165 lines (163 loc) 4.32 kB
// src/server/sprites.ts import { execFile, spawn } from "child_process"; import { promisify } from "util"; var execFileAsync = promisify(execFile); async function runSpriteCmd(args) { try { const { stdout } = await execFileAsync("sprite", args); return stdout.trim(); } catch (err) { throw new Error( `sprite ${args[0]} failed (exit ${err.code ?? "?"}): ${err.stderr ?? err.message}` ); } } async function runSpriteExec(spriteName, shellCommand, extraArgs = []) { return runSpriteCmd([ "exec", "-s", spriteName, ...extraArgs, "--", "bash", "-c", shellCommand ]); } async function runSpriteCopy(localPath, spriteName, remotePath) { try { await execFileAsync("sprite", [ "exec", "-s", spriteName, "--file", `${localPath}:${remotePath}`, "echo", "uploaded" ]); } catch (err) { throw new Error( `sprite file upload failed (exit ${err.code ?? "?"}): ${err.stderr ?? err.message}` ); } } var SPRITE_ENSURE_BUN_SH = ` set -e export PATH="$HOME/.bun/bin:$PATH" if ! command -v bun >/dev/null 2>&1; then curl -fsSL https://bun.sh/install | bash export PATH="$HOME/.bun/bin:$PATH" fi command -v bun `.trim(); function spriteStopServiceSh(service, pkillPattern) { return ` export PATH="$HOME/.bun/bin:$PATH" ENV="/.sprite/bin/sprite-env" $ENV services delete ${service} 2>/dev/null || true # Bracket trick / path pattern: avoid matching this bash -c argv. pkill -f "${pkillPattern}" 2>/dev/null || true sleep 2 exit 0 `.trim(); } function spriteStartServiceSh(opts) { const { service, bun, script, dir, port } = opts; return ` export PATH="$HOME/.bun/bin:$PATH" ENV="/.sprite/bin/sprite-env" BUN="${bun}" $ENV services create ${service} \\ --cmd "$BUN" \\ --args run,${script} \\ --dir ${dir} \\ --http-port ${port} \\ --no-stream `.trim(); } var SPRITE_STOP_TRELLIS_DB_SH = spriteStopServiceSh( "trellis-db", // Bracket so this bash -c argv does not match the pkill regex (else exit 143). "[t]rellis-db/server.js" ); function spriteStartTrellisDbSh(opts) { return spriteStartServiceSh({ service: "trellis-db", bun: opts.bun, script: "server.js", dir: "/home/sprite/trellis-db", port: opts.port }); } function parseSpriteUrlOutput(stdout) { const match = stdout.match(/URL:\s*(https:\/\/\S+)/i); if (!match) { throw new Error(`Could not parse sprite URL from output: ${stdout.trim()}`); } return match[1].replace(/\/$/, ""); } async function resolveSpritePublicUrl(spriteName) { const out = await runSpriteCmd(["url", "-s", spriteName]); return parseSpriteUrlOutput(out); } async function ensureSpritePublicAccess(spriteName) { try { await runSpriteCmd(["url", "update", "--auth", "public", "-s", spriteName]); } catch (err) { const msg = String(err.message ?? err); if (/already|unchanged|public/i.test(msg)) return; throw err; } } async function ensureSprite(spriteName) { try { await runSpriteCmd(["create", spriteName, "--skip-console"]); } catch (err) { const msg = String(err.message ?? err); if (/already exists|duplicate|conflict/i.test(msg)) return; throw err; } } async function runSpriteInteractive(args) { return new Promise((resolve, reject) => { const proc = spawn("sprite", args, { stdio: "inherit" }); proc.on("close", (code) => { if (code !== 0) reject(new Error(`sprite ${args[0]} failed (exit ${code})`)); else resolve(); }); proc.on("error", reject); }); } async function assertSpriteCli() { await runSpriteCmd(["--version"]).catch(() => { throw new Error( "`sprite` CLI not found. Install it from https://docs.sprites.dev and authenticate." ); }); } function resolveSprite(explicitName) { if (explicitName) { return explicitName; } throw new Error( "No sprite specified. Use --sprite <name> or set an active sprite with `trellis vm use <name>`" ); } export { runSpriteCmd, runSpriteExec, runSpriteCopy, SPRITE_ENSURE_BUN_SH, spriteStopServiceSh, spriteStartServiceSh, SPRITE_STOP_TRELLIS_DB_SH, spriteStartTrellisDbSh, parseSpriteUrlOutput, resolveSpritePublicUrl, ensureSpritePublicAccess, ensureSprite, runSpriteInteractive, assertSpriteCli, resolveSprite };