UNPKG

@mastra/core

Version:
446 lines (445 loc) 14.7 kB
const require_request_context = require("./request-context-ByoZMp-j.cjs"); const require_background_tasks = require("./background-tasks-lifNqs9M.cjs"); const require_workflow_event_processor = require("./workflow-event-processor-CkjVcesJ.cjs"); const require_scheduler = require("./scheduler-D9Mqp8B5.cjs"); const require_pull_transport = require("./pull-transport-BlEVfOcc.cjs"); //#region src/worker/strategies/http-remote-strategy.ts /** * Executes workflow steps by calling a remote server endpoint over HTTP. * Used in standalone worker deployments where the worker runs orchestration * logic but delegates actual step execution to the server. * * Authentication piggy-backs on Mastra's existing auth pipeline: the route * is marked `requiresAuth: true` and the deployer's `authenticateToken` * provider validates the credential we send here. There is no separate * "worker secret" — whatever auth scheme the rest of the server uses is * what the worker uses too. */ var HttpRemoteStrategy = class HttpRemoteStrategy { #baseUrl; #auth; #timeoutMs; constructor({ serverUrl, auth, timeoutMs }) { const normalized = serverUrl.endsWith("/") ? serverUrl : `${serverUrl}/`; this.#baseUrl = new URL(normalized); this.#auth = auth ?? HttpRemoteStrategy.#authFromEnv(); this.#timeoutMs = timeoutMs ?? 3e4; } /** * Default credential resolution: when `MASTRA_WORKER_AUTH_TOKEN` is set, * send it as a bearer token. The server's auth provider decides whether * to accept it. */ static #authFromEnv() { const token = process.env.MASTRA_WORKER_AUTH_TOKEN; if (!token) return void 0; return { type: "bearer", token }; } async executeStep(params) { const url = new URL(`workflows/${encodeURIComponent(params.workflowId)}/runs/${encodeURIComponent(params.runId)}/steps/execute`, this.#baseUrl); const body = this.#buildBody(params); const signal = this.#combineSignals(params.abortSignal); const res = await fetch(url, { method: "POST", headers: { "content-type": "application/json", ...this.#buildAuthHeaders() }, body, signal }); if (!res.ok) { const text = await res.text(); throw new StepExecutionError(res.status, text); } return res.json(); } /** * Build a JSON-serializable request body. The `params.requestContext` is * a plain object; if a caller stuffed a non-serializable value into it we * surface a clear error instead of silently dropping fields. * * `abortSignal` is consumed via fetch's `signal` argument — it must not * be in the body. */ #buildBody(params) { const { abortSignal: _abortSignal, requestContext, ...rest } = params; let safeRequestContext; try { safeRequestContext = JSON.parse(JSON.stringify(requestContext ?? {})); } catch (err) { throw new Error(`HttpRemoteStrategy: requestContext is not JSON-serializable. ${err instanceof Error ? err.message : String(err)}`); } return JSON.stringify({ ...rest, requestContext: safeRequestContext }); } #combineSignals(externalSignal) { const timeoutSignal = AbortSignal.timeout(this.#timeoutMs); if (!externalSignal) return timeoutSignal; if (typeof AbortSignal.any === "function") return AbortSignal.any([timeoutSignal, externalSignal]); const controller = new AbortController(); const onAbort = (reason) => controller.abort(reason); if (externalSignal.aborted) onAbort(externalSignal.reason); else externalSignal.addEventListener("abort", () => onAbort(externalSignal.reason), { once: true }); if (timeoutSignal.aborted) onAbort(timeoutSignal.reason); else timeoutSignal.addEventListener("abort", () => onAbort(timeoutSignal.reason), { once: true }); return controller.signal; } #buildAuthHeaders() { if (!this.#auth) return {}; if (this.#auth.type === "api-key") return { "x-worker-api-key": this.#auth.key }; if (this.#auth.type === "header") return { [this.#auth.name]: this.#auth.value }; return { authorization: `Bearer ${this.#auth.token}` }; } }; var StepExecutionError = class extends Error { status; body; constructor(status, body) { super(`Step execution failed with status ${status}: ${body}`); this.name = "StepExecutionError"; this.status = status; this.body = body; } }; //#endregion //#region src/worker/workers/orchestration-worker.ts const DEFAULT_GROUP = "mastra-orchestration"; /** * Processes workflow events (step.run, step.end, start, cancel, etc.) * by delegating to the WorkflowEventProcessor. * * Subscribes to the PubSub "workflows" topic and routes events to WEP. * * When MASTRA_STEP_EXECUTION_URL is set, injects HttpRemoteStrategy into * WEP so step execution happens over HTTP to the server. Otherwise WEP * executes steps directly in-process. */ var OrchestrationWorker = class extends require_pull_transport.MastraWorker { name = "orchestration"; #config; #transport; #processor; #strategy; #running = false; constructor(config = {}) { super(); this.#config = config; } async init(deps) { await super.init(deps); if (!deps.mastra) throw new Error("OrchestrationWorker requires Mastra instance"); const modes = deps.pubsub.supportedModes ?? ["pull"]; if (!modes.includes("pull")) throw new Error(`OrchestrationWorker requires a pull-capable PubSub, but the configured pubsub only supports: ${modes.join(", ")}. Either remove OrchestrationWorker from the workers list or use a pull-capable PubSub (e.g. Redis Streams).`); const remoteUrl = process.env.MASTRA_STEP_EXECUTION_URL; if (remoteUrl) this.#strategy = new HttpRemoteStrategy({ serverUrl: remoteUrl }); this.#processor = new require_workflow_event_processor.WorkflowEventProcessor({ mastra: deps.mastra, stepExecutionStrategy: this.#strategy }); } async start() { if (this.#running) return; if (!this.deps) throw new Error("OrchestrationWorker: call init() before start()"); const group = this.#config.group ?? DEFAULT_GROUP; this.#transport = new require_pull_transport.PullTransport({ pubsub: this.deps.pubsub, group, logger: this.deps.logger }); await this.#transport.start({ route: (event, ack, nack) => this.#processEvent(event, ack, nack) }); this.#running = true; } async stop() { if (!this.#running) return; try { if (this.#transport) { await this.#transport.stop(); this.#transport = void 0; } } finally { this.#running = false; } } get isRunning() { return this.#running; } async #processEvent(event, ack, nack) { if (!this.#processor) throw new Error("OrchestrationWorker not initialized"); const result = await this.#processor.handle(event); if (result.ok) { try { await ack?.(); } catch (e) { this.deps?.logger?.error("OrchestrationWorker: error acking event", { error: e }); } return; } this.deps?.logger?.error("OrchestrationWorker: error processing event", { type: event.type, runId: event.runId, retry: result.retry }); if (result.retry) { if (nack) try { await nack(); } catch (e) { this.deps?.logger?.error("OrchestrationWorker: error nacking event", { error: e }); } return; } if (ack) try { await ack(); } catch (e) { this.deps?.logger?.error("OrchestrationWorker: error acking terminal event", { error: e }); } } }; //#endregion //#region src/worker/workers/scheduler-worker.ts /** * Drives cron-based workflow schedules. On each tick it polls storage * for due schedules, computes next fire times, and publishes * workflow.start events. Does not consume events — only produces them. * * This is the **single** scheduler code path. The Mastra constructor * adds the worker to the default workers list (guarded by * `#shouldEnableScheduler()`), and `startWorkers()` initializes it. */ var SchedulerWorker = class extends require_pull_transport.MastraWorker { name = "scheduler"; #scheduler; #config; #running = false; constructor(config = {}) { super(); this.#config = config; } async init(deps) { await super.init(deps); if (!deps.storage) { deps.logger.warn("SchedulerWorker: no storage configured, scheduler will not run"); return; } const schedulesStore = await deps.storage.getStore("schedules"); if (!schedulesStore) { deps.logger.warn("SchedulerWorker: no schedules store available, scheduler will not run"); return; } const mastra = this.mastra; const isTargetReady = mastra ? (target) => { try { if (target.type === "workflow") { mastra.getWorkflowById(target.workflowId); return true; } if (target.type === "agent") { mastra.getAgentById(target.agentId); return true; } return false; } catch { return false; } } : void 0; this.#scheduler = new require_scheduler.Scheduler({ schedulesStore, pubsub: deps.pubsub, config: { ...this.#config, isTargetReady } }); this.#scheduler.__setLogger(deps.logger); if (this.mastra) try { await this.mastra.registerDeclarativeSchedules(schedulesStore); } catch (err) { deps.logger.error?.("SchedulerWorker: failed to register declarative schedules", { error: err }); } } async start() { if (this.#running) return; if (this.#scheduler) await this.#scheduler.start(); this.#running = true; } async stop() { if (!this.#running) return; if (this.#scheduler) await this.#scheduler.stop(); this.#running = false; } get isRunning() { return this.#running; } /** Expose the underlying scheduler for direct API access (e.g., schedule management). */ get scheduler() { return this.#scheduler; } }; //#endregion //#region src/worker/workers/background-task-worker.ts /** * Manages background tool execution for agents. Handles task queuing, * concurrency limits, and lifecycle. Subscribes to PubSub internally * via BackgroundTaskManager's own subscription mechanism. */ var BackgroundTaskWorker = class extends require_pull_transport.MastraWorker { name = "backgroundTasks"; #manager; #ownsManager = false; #config; #running = false; constructor(config = {}) { super(); this.#config = config; } async init(deps) { await super.init(deps); const existing = deps.mastra?.backgroundTaskManager; if (existing) { this.#manager = existing; this.#ownsManager = false; return; } this.#manager = new require_background_tasks.BackgroundTaskManager({ enabled: true, mode: "worker", globalConcurrency: this.#config.globalConcurrency, perAgentConcurrency: this.#config.perAgentConcurrency, backpressure: this.#config.backpressure, defaultTimeoutMs: this.#config.defaultTimeoutMs }); this.#ownsManager = true; if (deps.mastra) { this.#manager.__registerMastra(deps.mastra); this.#wireStaticTools(deps.mastra); } } /** * Populate the manager's static executor registry from tools registered * on `Mastra`, so that cross-process dispatches can be resolved by tool * name on this worker. Mirrors the wiring Mastra does for its own * managed background-task manager — the worker owns a separate manager * instance, so it has to populate its own registry. */ #wireStaticTools(mastra) { const tools = mastra.listTools?.call(mastra); if (!tools || !this.#manager) return; for (const [name, tool] of Object.entries(tools)) { if (!tool || typeof tool.execute !== "function") continue; const execute = tool.execute.bind(tool); this.#manager.registerStaticExecutor(name, { execute: async (args, options) => { return execute(args, { toolCallId: "", messages: [], abortSignal: options?.abortSignal }); } }); } } async start() { if (this.#running) return; if (!this.#manager || !this.deps) throw new Error("BackgroundTaskWorker: call init() before start()"); if (this.#ownsManager) await this.#manager.init(this.deps.pubsub); this.#running = true; } async stop() { if (!this.#running) return; if (this.#manager && this.#ownsManager) await this.#manager.shutdown(); this.#running = false; } get isRunning() { return this.#running; } /** Expose the underlying manager for direct API access. */ get manager() { return this.#manager; } }; //#endregion //#region src/worker/strategies/in-process-strategy.ts /** * Executes workflow steps in the same process by delegating to StepExecutor. * This is the default strategy used when the worker runs co-located with the server. */ var InProcessStrategy = class { #mastra; constructor({ mastra } = {}) { this.#mastra = mastra; } __registerMastra(mastra) { this.#mastra = mastra; } async executeStep(params) { if (!this.#mastra) throw new Error("InProcessStrategy requires Mastra instance. Call __registerMastra() first."); const entry = require_workflow_event_processor.getStepEntry(this.#mastra.getWorkflowById(params.workflowId), params.executionPath); if (!entry) throw new Error(`InProcessStrategy: could not resolve step "${params.stepId}" at executionPath [${params.executionPath.join(",")}] in workflow "${params.workflowId}"`); const rc = new require_request_context.RequestContext(Object.entries(params.requestContext ?? {})); let abortController; if (params.abortSignal) { abortController = new AbortController(); if (params.abortSignal.aborted) abortController.abort(params.abortSignal.reason); else params.abortSignal.addEventListener("abort", () => { abortController.abort(params.abortSignal.reason); }, { once: true }); } return new require_workflow_event_processor.StepExecutor({ mastra: this.#mastra }).execute({ workflowId: params.workflowId, entry, runId: params.runId, stepResults: params.stepResults, state: params.state, requestContext: rc, input: params.input, resumeData: params.resumeData, retryCount: params.retryCount, foreachIdx: params.foreachIdx, validateInputs: params.validateInputs, abortController, format: params.format, perStep: params.perStep }); } }; //#endregion Object.defineProperty(exports, "BackgroundTaskWorker", { enumerable: true, get: function() { return BackgroundTaskWorker; } }); Object.defineProperty(exports, "HttpRemoteStrategy", { enumerable: true, get: function() { return HttpRemoteStrategy; } }); Object.defineProperty(exports, "InProcessStrategy", { enumerable: true, get: function() { return InProcessStrategy; } }); Object.defineProperty(exports, "OrchestrationWorker", { enumerable: true, get: function() { return OrchestrationWorker; } }); Object.defineProperty(exports, "SchedulerWorker", { enumerable: true, get: function() { return SchedulerWorker; } }); Object.defineProperty(exports, "StepExecutionError", { enumerable: true, get: function() { return StepExecutionError; } }); //# sourceMappingURL=worker-H14yjTvH.cjs.map