UNPKG

trigger.dev

Version:

A Command-Line Interface for Trigger.dev (v3) projects

304 lines 13.2 kB
import { clock, logger, runtime, taskCatalog, TaskRunErrorCodes, WorkerToExecutorMessageCatalog, WorkerManifest, ExecutorToWorkerMessageCatalog, timeout, runMetadata, waitUntil, apiClientManager, } from "@trigger.dev/core/v3"; import { TriggerTracer } from "@trigger.dev/core/v3/tracer"; import { DevRuntimeManager } from "@trigger.dev/core/v3/dev"; import { ConsoleInterceptor, DevUsageManager, UsageTimeoutManager, DurableClock, getEnvVar, logLevels, OtelTaskLogger, StandardTaskCatalog, TaskExecutor, TracingSDK, usage, getNumberEnvVar, StandardMetadataManager, StandardWaitUntilManager, } from "@trigger.dev/core/v3/workers"; import { ZodIpcConnection } from "@trigger.dev/core/v3/zodIpc"; import { readFile } from "node:fs/promises"; import sourceMapSupport from "source-map-support"; import { VERSION } from "../version.js"; import { env } from "std-env"; import { normalizeImportPath } from "../utilities/normalizeImportPath.js"; sourceMapSupport.install({ handleUncaughtExceptions: false, environment: "node", hookRequire: false, }); process.on("uncaughtException", function (error, origin) { if (error instanceof Error) { process.send && process.send({ type: "UNCAUGHT_EXCEPTION", payload: { error: { name: error.name, message: error.message, stack: error.stack }, origin, }, version: "v1", }); } else { process.send && process.send({ type: "UNCAUGHT_EXCEPTION", payload: { error: { name: "Error", message: typeof error === "string" ? error : JSON.stringify(error), }, origin, }, version: "v1", }); } }); taskCatalog.setGlobalTaskCatalog(new StandardTaskCatalog()); const durableClock = new DurableClock(); clock.setGlobalClock(durableClock); const devUsageManager = new DevUsageManager(); usage.setGlobalUsageManager(devUsageManager); const devRuntimeManager = new DevRuntimeManager(); runtime.setGlobalRuntimeManager(devRuntimeManager); timeout.setGlobalManager(new UsageTimeoutManager(devUsageManager)); const runMetadataManager = new StandardMetadataManager(apiClientManager.clientOrThrow(), getEnvVar("TRIGGER_STREAM_URL", getEnvVar("TRIGGER_API_URL")) ?? "https://api.trigger.dev", (getEnvVar("TRIGGER_REALTIME_STREAM_VERSION") ?? "v1")); runMetadata.setGlobalManager(runMetadataManager); const waitUntilManager = new StandardWaitUntilManager(); waitUntil.setGlobalManager(waitUntilManager); // Wait for all streams to finish before completing the run waitUntil.register({ requiresResolving: () => runMetadataManager.hasActiveStreams(), promise: () => runMetadataManager.waitForAllStreams(), }); const triggerLogLevel = getEnvVar("TRIGGER_LOG_LEVEL"); async function importConfig(configPath) { const configModule = await import(normalizeImportPath(configPath)); const config = configModule?.default ?? configModule?.config; return { config, handleError: configModule?.handleError, }; } async function loadWorkerManifest() { const manifestContents = await readFile(env.TRIGGER_WORKER_MANIFEST_PATH, "utf-8"); const raw = JSON.parse(manifestContents); return WorkerManifest.parse(raw); } async function bootstrap() { const workerManifest = await loadWorkerManifest(); const { config, handleError } = await importConfig(workerManifest.configPath); const tracingSDK = new TracingSDK({ url: env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://0.0.0.0:4318", instrumentations: config.instrumentations ?? [], diagLogLevel: env.OTEL_LOG_LEVEL ?? "none", forceFlushTimeoutMillis: 30_000, }); const otelTracer = tracingSDK.getTracer("trigger-dev-worker", VERSION); const otelLogger = tracingSDK.getLogger("trigger-dev-worker", VERSION); const tracer = new TriggerTracer({ tracer: otelTracer, logger: otelLogger }); const consoleInterceptor = new ConsoleInterceptor(otelLogger, typeof config.enableConsoleLogging === "boolean" ? config.enableConsoleLogging : true); const configLogLevel = triggerLogLevel ?? config.logLevel ?? "info"; const otelTaskLogger = new OtelTaskLogger({ logger: otelLogger, tracer: tracer, level: logLevels.includes(configLogLevel) ? configLogLevel : "info", }); logger.setGlobalTaskLogger(otelTaskLogger); for (const task of workerManifest.tasks) { taskCatalog.registerTaskFileMetadata(task.id, { exportName: task.exportName, filePath: task.filePath, entryPoint: task.entryPoint, }); } return { tracer, tracingSDK, consoleInterceptor, config, handleErrorFn: handleError, workerManifest, }; } let _execution; let _isRunning = false; let _tracingSDK; const zodIpc = new ZodIpcConnection({ listenSchema: WorkerToExecutorMessageCatalog, emitSchema: ExecutorToWorkerMessageCatalog, process, handlers: { EXECUTE_TASK_RUN: async ({ execution, traceContext, metadata }, sender) => { if (_isRunning) { console.error("Worker is already running a task"); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.TASK_ALREADY_RUNNING, }, usage: { durationMs: 0, }, taskIdentifier: execution.task.id, }, }); return; } const { tracer, tracingSDK, consoleInterceptor, config, handleErrorFn, workerManifest } = await bootstrap(); _tracingSDK = tracingSDK; const taskManifest = workerManifest.tasks.find((t) => t.id === execution.task.id); if (!taskManifest) { console.error(`Could not find task ${execution.task.id}`); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.COULD_NOT_FIND_TASK, }, usage: { durationMs: 0, }, taskIdentifier: execution.task.id, }, }); return; } try { await import(normalizeImportPath(taskManifest.entryPoint)); } catch (err) { console.error(`Failed to import task ${execution.task.id}`, err); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.COULD_NOT_IMPORT_TASK, message: err instanceof Error ? err.message : String(err), stackTrace: err instanceof Error ? err.stack : undefined, }, usage: { durationMs: 0, }, taskIdentifier: execution.task.id, }, }); return; } process.title = `trigger-dev-worker: ${execution.task.id} ${execution.run.id}`; // Import the task module const task = taskCatalog.getTask(execution.task.id); if (!task) { console.error(`Could not find task ${execution.task.id}`); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.COULD_NOT_FIND_EXECUTOR, }, usage: { durationMs: 0, }, taskIdentifier: execution.task.id, }, }); return; } const executor = new TaskExecutor(task, { tracer, tracingSDK, consoleInterceptor, config, handleErrorFn, }); try { _execution = execution; _isRunning = true; runMetadataManager.runId = execution.run.id; runMetadataManager.startPeriodicFlush(getNumberEnvVar("TRIGGER_RUN_METADATA_FLUSH_INTERVAL", 1000)); const measurement = usage.start(); // This lives outside of the executor because this will eventually be moved to the controller level const signal = execution.run.maxDuration ? timeout.abortAfterTimeout(execution.run.maxDuration) : undefined; signal?.addEventListener("abort", async (e) => { if (_isRunning) { _isRunning = false; _execution = undefined; const usageSample = usage.stop(measurement); await sender.send("TASK_RUN_COMPLETED", { execution, result: { ok: false, id: execution.run.id, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.MAX_DURATION_EXCEEDED, message: signal.reason instanceof Error ? signal.reason.message : String(signal.reason), }, usage: { durationMs: usageSample.cpuTime, }, taskIdentifier: execution.task.id, }, }); } }); const { result } = await executor.execute(execution, metadata, traceContext, measurement, signal); const usageSample = usage.stop(measurement); if (_isRunning) { return sender.send("TASK_RUN_COMPLETED", { execution, result: { ...result, usage: { durationMs: usageSample.cpuTime, }, taskIdentifier: execution.task.id, }, }); } } finally { _execution = undefined; _isRunning = false; } }, TASK_RUN_COMPLETED_NOTIFICATION: async (payload) => { switch (payload.version) { case "v1": { devRuntimeManager.resumeTask(payload.completion, payload.execution.run.id); break; } case "v2": { devRuntimeManager.resumeTask(payload.completion, payload.completion.id); break; } } }, FLUSH: async ({ timeoutInMs }, sender) => { await Promise.allSettled([_tracingSDK?.flush(), runMetadataManager.flush()]); }, }, }); process.title = "trigger-dev-worker"; async function asyncHeartbeat(initialDelayInSeconds = 30, intervalInSeconds = 30) { async function _doHeartbeat() { while (true) { if (_isRunning && _execution) { try { await zodIpc.send("TASK_HEARTBEAT", { id: _execution.attempt.id }); } catch (err) { console.error("Failed to send HEARTBEAT message", err); } } await new Promise((resolve) => setTimeout(resolve, 1000 * intervalInSeconds)); } } // Wait for the initial delay await new Promise((resolve) => setTimeout(resolve, 1000 * initialDelayInSeconds)); // Wait for 5 seconds before the next execution return _doHeartbeat(); } await asyncHeartbeat(); //# sourceMappingURL=dev-run-worker.js.map