UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev projects

199 lines 7.08 kB
import { z } from "zod"; import { x } from "tinyexec"; import stripAnsi from "strip-ansi"; import { toolsMetadata } from "../config.js"; import { respondWithError, toolHandler } from "../utils.js"; import { resolveCLIExec } from "./deploys.js"; // State for the running dev server process let devProcess = null; let devState = "stopped"; let devLogs = []; let devCwd; const MAX_LOG_LINES = 200; function getDevState() { return devState; } function pushLog(line) { devLogs.push(line); if (devLogs.length > MAX_LOG_LINES) { devLogs = devLogs.slice(-MAX_LOG_LINES); } } const StartDevServerInput = { configPath: z .string() .describe("The path to the trigger.config.ts file or project directory. Only needed when the trigger.config.ts file is not at the root dir.") .optional(), }; export const startDevServerTool = { name: toolsMetadata.start_dev_server.name, title: toolsMetadata.start_dev_server.title, description: toolsMetadata.start_dev_server.description, inputSchema: StartDevServerInput, handler: toolHandler(StartDevServerInput, async (input, { ctx }) => { ctx.logger?.log("calling start_dev_server", { input }); if (devProcess && devState !== "stopped") { return { content: [ { type: "text", text: `Dev server is already ${devState}. Use \`dev_server_status\` to see output or \`stop_dev_server\` to stop it.`, }, ], }; } const cwd = await ctx.getProjectDir({ cwd: input.configPath }); if (!cwd.ok) { return respondWithError(cwd.error); } const auth = await ctx.getAuth(); const args = ["dev", "--api-url", auth.auth.apiUrl, "--skip-mcp-install"]; if (ctx.options.profile) { args.push("--profile", ctx.options.profile); } const [nodePath, cliPath] = await resolveCLIExec(ctx, cwd.cwd); ctx.logger?.log("start_dev_server args", { nodePath, cliPath, args, cwd: cwd.cwd }); // Reset state devState = "starting"; devLogs = []; devCwd = cwd.cwd; devProcess = x(nodePath, [cliPath, ...args], { nodeOptions: { cwd: cwd.cwd, env: { TRIGGER_MCP_SERVER: "1", }, }, }); // Stream output in the background const myProcess = devProcess; const readOutput = async () => { try { for await (const line of myProcess) { const clean = stripAnsi(line); pushLog(clean); if (clean.includes("Local worker ready")) { devState = "ready"; } if (clean.includes("Build failed")) { devState = "error"; } } } catch { // Process ended } // Only update module state if no new server has been started since if (devProcess !== myProcess) { return; } const exitCode = myProcess.exitCode; if (devState !== "error") { devState = "stopped"; } pushLog(`[process exited with code ${exitCode ?? "unknown"}]`); devProcess = null; }; // Don't await — let it run in the background readOutput(); // Wait up to 30s for "ready" or "error" const startTime = Date.now(); const TIMEOUT_MS = 30_000; while (Date.now() - startTime < TIMEOUT_MS) { const state = getDevState(); if (state === "ready" || state === "error" || state === "stopped") { break; } await new Promise((r) => setTimeout(r, 500)); } const recentLogs = devLogs.slice(-30).join("\n"); const finalState = getDevState(); if (finalState === "ready") { return { content: [ { type: "text", text: `Dev server is ready.\n\n\`\`\`\n${recentLogs}\n\`\`\``, }, ], }; } if (finalState === "error") { return { content: [ { type: "text", text: `Dev server started but has build errors:\n\n\`\`\`\n${recentLogs}\n\`\`\``, }, ], }; } if (finalState === "starting") { return { content: [ { type: "text", text: `Dev server is still starting (timed out after 30s). Use \`dev_server_status\` to check progress.\n\n\`\`\`\n${recentLogs}\n\`\`\``, }, ], }; } return respondWithError(`Dev server stopped unexpectedly:\n${recentLogs}`); }), }; export const stopDevServerTool = { name: toolsMetadata.stop_dev_server.name, title: toolsMetadata.stop_dev_server.title, description: toolsMetadata.stop_dev_server.description, inputSchema: {}, handler: toolHandler({}, async (_input, { ctx }) => { ctx.logger?.log("calling stop_dev_server"); if (!devProcess || devState === "stopped") { return { content: [{ type: "text", text: "Dev server is not running." }], }; } devProcess.kill(); devState = "stopped"; devProcess = null; return { content: [{ type: "text", text: "Dev server stopped." }], }; }), }; export const devServerStatusTool = { name: toolsMetadata.dev_server_status.name, title: toolsMetadata.dev_server_status.title, description: toolsMetadata.dev_server_status.description, inputSchema: { lines: z .number() .int() .default(50) .describe("Number of recent log lines to return. Defaults to 50."), }, handler: toolHandler({ lines: z.number().int().default(50), }, async (input, { ctx }) => { ctx.logger?.log("calling dev_server_status"); const recentLogs = devLogs.slice(-input.lines).join("\n"); const content = [`## Dev Server Status: ${devState}`, ""]; if (devCwd) { content.push(`**Directory:** ${devCwd}`); } content.push(`**Log buffer:** ${devLogs.length} lines`); content.push(""); if (recentLogs) { content.push("```"); content.push(recentLogs); content.push("```"); } else { content.push("_No output yet_"); } return { content: [{ type: "text", text: content.join("\n") }], }; }), }; //# sourceMappingURL=devServer.js.map