diff --git a/toolchain/mcp/ac-venue-mcp.mjs b/toolchain/mcp/ac-venue-mcp.mjs index 182f16e61b..425adbb8c3 100644 --- a/toolchain/mcp/ac-venue-mcp.mjs +++ b/toolchain/mcp/ac-venue-mcp.mjs @@ -179,7 +179,8 @@ async function runQueueOnce() { const mark = (status, extra = {}) => { const qq = queue(); const it = qq.items.find((i) => i.id === item.id); if (it) Object.assign(it, { status, ...extra }); saveQueue(qq); }; try { setRig({ owner: local, since: new Date().toISOString(), note: `setlist: ${item.label || item.out}` }); - if (item.status !== "ready") { mark("preparing"); runnerNote = `preparing ${item.label || item.out}`; await prepare({ score: item.score, out: item.out, stage: true, keepPiece: false, allowPieces: item.allowPieces || "notespatial-controls,culturehub-rehearsal,red,connection-check,connection-controls,say" }); } + const ownConductor = existsSync(join(outDir(item.out), "run-fleet.py")); // a folder with its own conductor stages itself + if (item.status !== "ready" && !ownConductor) { mark("preparing"); runnerNote = `preparing ${item.label || item.out}`; await prepare({ score: item.score, out: item.out, stage: true, keepPiece: false, allowPieces: item.allowPieces || "notespatial-controls,culturehub-rehearsal,red,connection-check,connection-controls,say" }); } mark("checking"); runnerNote = `checking ${item.label || item.out}`; const c = await check(item.out); if (!c.ready) throw new Error(`not ready: ${c.tail.join(" | ")}`); mark("cueing"); const r = await cue({ out: item.out, announce: item.announce, rttMax: item.rttMax || 0.05 }); mark("playing", { runId: r.runId, startedAt: Date.now() / 1000 }); runnerNote = `playing ${item.label || item.out} (${r.runId})`; @@ -251,4 +252,4 @@ async function handleMessage(msg) { } const port = httpPort(process.argv, 0); if (port) serveHttp({ handleMessage, port, banner: "🎪 ac-venue-mcp shared daemon" }); -else serveStdio({ handleMessage, banner: "🎪 ac-venue-mcp started (venue_status, venue_prepare, venue_check, venue_cue, venue_result, venue_stop, venue_claim)" }); +else serveStdio({ handleMessage, banner: "🎪 ac-venue-mcp started (venue_enqueue, venue_setlist, venue_dequeue, venue_autoplay, venue_next, venue_status, venue_prepare, venue_check, venue_cue, venue_result, venue_stop, venue_claim)" }); diff --git a/toolchain/mcp/venue-cli.mjs b/toolchain/mcp/venue-cli.mjs new file mode 100644 index 0000000000..dafd28e8a1 --- /dev/null +++ b/toolchain/mcp/venue-cli.mjs @@ -0,0 +1,24 @@ +#!/usr/bin/env node +// venue-cli.mjs — call one ac-venue tool from a shell, no JSON quoting. +// +// node toolchain/mcp/venue-cli.mjs venue_setlist +// node toolchain/mcp/venue-cli.mjs venue_enqueue out=/Users/jas/Shelf/set-midi-01-rush-e label="Rush E" +// node toolchain/mcp/venue-cli.mjs venue_autoplay on=true +// node toolchain/mcp/venue-cli.mjs venue_next (blocks until that pass ends) +// +// Arguments are key=value (true/false/numbers parsed) or one JSON object. +// Runs the MCP as a child on this machine: the queue, rig file and tunnels are +// this machine's, so run it on the conductor (over ssh from elsewhere). +import { spawn } from "node:child_process"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; +const [tool, ...rest] = process.argv.slice(2); +if (!tool) { console.error("usage: venue-cli.mjs [key=value … | '{json}']"); process.exit(2); } +let args = {}; +if (rest.length === 1 && rest[0].trim().startsWith("{")) args = JSON.parse(rest[0]); +else for (const kv of rest) { const i = kv.indexOf("="); if (i < 0) continue; const k = kv.slice(0, i), v = kv.slice(i + 1); args[k] = v === "true" ? true : v === "false" ? false : (v !== "" && !isNaN(Number(v)) ? Number(v) : v); } +const child = spawn(process.execPath, [join(dirname(fileURLToPath(import.meta.url)), "ac-venue-mcp.mjs")], { stdio: ["pipe", "pipe", "inherit"] }); +let buf = ""; +child.stdout.on("data", (d) => { buf += d; let i; while ((i = buf.indexOf("\n")) >= 0) { const line = buf.slice(0, i); buf = buf.slice(i + 1); if (!line.startsWith("{")) continue; const m = JSON.parse(line); if (m.id === 2) { const c = m.result?.content?.[0]?.text ?? JSON.stringify(m.error ?? m); console.log(c); child.kill(); process.exit(m.result?.isError ? 1 : 0); } } }); +child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: 1, method: "initialize", params: {} }) + "\n"); +child.stdin.write(JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/call", params: { name: tool, arguments: args } }) + "\n");