@microfox/ai-worker 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -1,19 +1,28 @@
1
1
  import {
2
2
  dispatch,
3
- dispatchLocal
4
- } from "./chunk-FQCZSXDI.mjs";
3
+ dispatchLocal,
4
+ dispatchQueue,
5
+ getWorkersTriggerUrl
6
+ } from "./chunk-BJPQY2NJ.mjs";
5
7
  import {
6
8
  clearWorkersConfigCache,
7
9
  getWorkersConfig,
8
10
  resolveQueueUrl
9
11
  } from "./chunk-ZYYWZ3PR.mjs";
10
12
  import {
11
- createLambdaHandler
12
- } from "./chunk-WVR4JVWK.mjs";
13
+ SQS_MAX_DELAY_SECONDS,
14
+ createLambdaHandler,
15
+ wrapHandlerForQueue
16
+ } from "./chunk-4WU5ZCHS.mjs";
13
17
  import {
14
18
  __require
15
19
  } from "./chunk-BJTO5JO5.mjs";
16
20
 
21
+ // src/queue.ts
22
+ function defineWorkerQueue(config) {
23
+ return config;
24
+ }
25
+
17
26
  // src/index.ts
18
27
  function createWorker(config) {
19
28
  const { id, inputSchema, outputSchema, handler } = config;
@@ -130,6 +139,42 @@ function createWorker(config) {
130
139
  });
131
140
  return null;
132
141
  }
142
+ },
143
+ appendInternalJob: async (entry) => {
144
+ try {
145
+ const nextJsPath = "@/app/api/workflows/stores/jobStore";
146
+ const explicitPath2 = process.env.WORKER_JOB_STORE_MODULE_PATH;
147
+ for (const importPath of [nextJsPath, explicitPath2].filter(Boolean)) {
148
+ try {
149
+ const module = await import(importPath);
150
+ if (typeof module?.appendInternalJob === "function") {
151
+ await module.appendInternalJob(localJobId, entry);
152
+ return;
153
+ }
154
+ } catch {
155
+ }
156
+ }
157
+ } catch (error) {
158
+ console.warn("[Worker] Failed to appendInternalJob (direct DB):", { localJobId, error: error?.message || String(error) });
159
+ }
160
+ },
161
+ getJob: async (otherJobId) => {
162
+ try {
163
+ const nextJsPath = "@/app/api/workflows/stores/jobStore";
164
+ const explicitPath2 = process.env.WORKER_JOB_STORE_MODULE_PATH;
165
+ for (const importPath of [nextJsPath, explicitPath2].filter(Boolean)) {
166
+ try {
167
+ const module = await import(importPath);
168
+ if (typeof module?.getJob === "function") {
169
+ return await module.getJob(otherJobId);
170
+ }
171
+ } catch {
172
+ }
173
+ }
174
+ } catch (error) {
175
+ console.warn("[Worker] Failed to getJob (direct DB):", { otherJobId, error: error?.message || String(error) });
176
+ }
177
+ return null;
133
178
  }
134
179
  };
135
180
  }
@@ -205,6 +250,101 @@ function createWorker(config) {
205
250
  };
206
251
  };
207
252
  const jobStore = createLocalJobStore(directJobStore, jobStoreUrl);
253
+ const DEFAULT_POLL_INTERVAL_MS = 2e3;
254
+ const DEFAULT_POLL_TIMEOUT_MS = 15 * 60 * 1e3;
255
+ const createLocalDispatchWorker = (parentJobId, parentWorkerId, parentContext, store) => {
256
+ return async (calleeWorkerId, input2, options2) => {
257
+ const childJobId = options2?.jobId || `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
258
+ const metadata = options2?.metadata ?? {};
259
+ const serializedContext = {};
260
+ if (parentContext.requestId) serializedContext.requestId = parentContext.requestId;
261
+ const messageBody = {
262
+ workerId: calleeWorkerId,
263
+ jobId: childJobId,
264
+ input: input2 ?? {},
265
+ context: serializedContext,
266
+ webhookUrl: options2?.webhookUrl,
267
+ metadata,
268
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
269
+ };
270
+ let triggerUrl;
271
+ try {
272
+ triggerUrl = getWorkersTriggerUrl();
273
+ } catch (e) {
274
+ throw new Error(
275
+ `Local dispatchWorker requires WORKER_BASE_URL (or similar) for worker "${calleeWorkerId}": ${e?.message ?? e}`
276
+ );
277
+ }
278
+ const headers = { "Content-Type": "application/json" };
279
+ const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
280
+ if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
281
+ if (options2?.await !== true && options2?.delaySeconds != null && options2.delaySeconds > 0) {
282
+ const sec = Math.min(SQS_MAX_DELAY_SECONDS, Math.max(0, Math.floor(options2.delaySeconds)));
283
+ const storeRef = store;
284
+ setTimeout(() => {
285
+ fetch(triggerUrl, {
286
+ method: "POST",
287
+ headers,
288
+ body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody })
289
+ }).then(async (response2) => {
290
+ if (!response2.ok) {
291
+ const text = await response2.text().catch(() => "");
292
+ console.error(
293
+ `[Worker] Delayed trigger failed for "${calleeWorkerId}": ${response2.status} ${response2.statusText}${text ? ` - ${text}` : ""}`
294
+ );
295
+ return;
296
+ }
297
+ if (storeRef?.appendInternalJob) {
298
+ await storeRef.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });
299
+ }
300
+ }).catch((err) => {
301
+ console.error("[Worker] Delayed trigger error:", { calleeWorkerId, jobId: childJobId, error: err?.message ?? err });
302
+ });
303
+ }, sec * 1e3);
304
+ return { jobId: childJobId, messageId: void 0 };
305
+ }
306
+ const response = await fetch(triggerUrl, {
307
+ method: "POST",
308
+ headers,
309
+ body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody })
310
+ });
311
+ if (!response.ok) {
312
+ const text = await response.text().catch(() => "");
313
+ throw new Error(
314
+ `Failed to trigger worker "${calleeWorkerId}": ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
315
+ );
316
+ }
317
+ const data = await response.json().catch(() => ({}));
318
+ const messageId = data?.messageId ? String(data.messageId) : `trigger-${childJobId}`;
319
+ if (store?.appendInternalJob) {
320
+ await store.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });
321
+ }
322
+ if (options2?.await && store?.getJob) {
323
+ const pollIntervalMs = options2.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
324
+ const pollTimeoutMs = options2.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;
325
+ const deadline = Date.now() + pollTimeoutMs;
326
+ while (Date.now() < deadline) {
327
+ const child = await store.getJob(childJobId);
328
+ if (!child) {
329
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
330
+ continue;
331
+ }
332
+ if (child.status === "completed") {
333
+ return { jobId: childJobId, messageId, output: child.output };
334
+ }
335
+ if (child.status === "failed") {
336
+ const err = child.error;
337
+ throw new Error(err?.message ?? `Child worker ${calleeWorkerId} failed`);
338
+ }
339
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
340
+ }
341
+ throw new Error(
342
+ `Child worker ${calleeWorkerId} (${childJobId}) did not complete within ${pollTimeoutMs}ms`
343
+ );
344
+ }
345
+ return { jobId: childJobId, messageId };
346
+ };
347
+ };
208
348
  if (directJobStore?.setJob) {
209
349
  try {
210
350
  await directJobStore.setJob(localJobId, {
@@ -222,10 +362,16 @@ function createWorker(config) {
222
362
  });
223
363
  }
224
364
  }
365
+ const baseContext = { jobId: localJobId, workerId: id };
225
366
  const handlerContext = {
226
- jobId: localJobId,
227
- workerId: id,
228
- ...jobStore ? { jobStore } : {}
367
+ ...baseContext,
368
+ ...jobStore ? { jobStore } : {},
369
+ dispatchWorker: createLocalDispatchWorker(
370
+ localJobId,
371
+ id,
372
+ baseContext,
373
+ jobStore
374
+ )
229
375
  };
230
376
  try {
231
377
  if (jobStore) {
@@ -301,13 +447,18 @@ function createLambdaEntrypoint(agent) {
301
447
  return createLambdaHandler(agent.handler, agent.outputSchema);
302
448
  }
303
449
  export {
450
+ SQS_MAX_DELAY_SECONDS,
304
451
  clearWorkersConfigCache,
305
452
  createLambdaEntrypoint,
306
453
  createLambdaHandler,
307
454
  createWorker,
455
+ defineWorkerQueue,
308
456
  dispatch,
309
457
  dispatchLocal,
458
+ dispatchQueue,
310
459
  getWorkersConfig,
311
- resolveQueueUrl
460
+ getWorkersTriggerUrl,
461
+ resolveQueueUrl,
462
+ wrapHandlerForQueue
312
463
  };
313
464
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["/**\n * @microfox/ai-worker\n * Worker runtime for ai-router - SQS-based async agent execution\n */\n\nimport { dispatch, dispatchLocal, type DispatchOptions, type DispatchResult } from './client.js';\nimport { createLambdaHandler, type WorkerHandler, type JobStore } from './handler.js';\nimport type { ZodType, z } from 'zod';\n\nexport * from './client.js';\nexport * from './handler.js';\nexport * from './config.js';\n\n/**\n * Schedule event configuration for a worker.\n * Supports both simple rate/cron strings and full configuration objects.\n * \n * @example Simple rate/cron\n * ```typescript\n * schedule: 'rate(2 hours)'\n * // or\n * schedule: 'cron(0 12 * * ? *)'\n * ```\n * \n * @example Full configuration\n * ```typescript\n * schedule: {\n * rate: 'rate(10 minutes)',\n * enabled: true,\n * input: { key1: 'value1' }\n * }\n * ```\n * \n * @example Multiple schedules\n * ```typescript\n * schedule: [\n * 'rate(2 hours)',\n * { rate: 'cron(0 12 * * ? *)', enabled: false }\n * ]\n * ```\n */\nexport interface ScheduleEventConfig {\n /**\n * Schedule rate using either rate() or cron() syntax.\n * Can be a string or array of strings for multiple schedules.\n * \n * @example 'rate(2 hours)' or 'cron(0 12 * * ? *)'\n * @example ['cron(0 0/4 ? * MON-FRI *)', 'cron(0 2 ? * SAT-SUN *)']\n */\n rate: string | string[];\n /**\n * Whether the schedule is enabled (default: true).\n */\n enabled?: boolean;\n /**\n * Input payload to pass to the function.\n */\n input?: Record<string, any>;\n /**\n * JSONPath expression to select part of the event data as input.\n */\n inputPath?: string;\n /**\n * Input transformer configuration for custom input mapping.\n */\n inputTransformer?: {\n inputPathsMap?: Record<string, string>;\n inputTemplate?: string;\n };\n /**\n * Name of the schedule event.\n */\n name?: string;\n /**\n * Description of the schedule event.\n */\n description?: string;\n /**\n * Method to use: 'eventBus' (default) or 'scheduler'.\n * Use 'scheduler' for higher limits (1M events vs 300).\n */\n method?: 'eventBus' | 'scheduler';\n /**\n * Timezone for the schedule (only used with method: 'scheduler').\n * @example 'America/New_York'\n */\n timezone?: string;\n}\n\nexport type ScheduleConfig = \n | string \n | ScheduleEventConfig \n | (string | ScheduleEventConfig)[];\n\n/**\n * Configuration for a worker's Lambda function deployment.\n * \n * **Best Practice**: Export this as a separate const from your worker file:\n * ```typescript\n * export const workerConfig: WorkerConfig = {\n * timeout: 900,\n * memorySize: 2048,\n * layers: ['arn:aws:lambda:${aws:region}:${aws:accountId}:layer:ffmpeg:1'],\n * schedule: 'rate(2 hours)',\n * };\n * ```\n * \n * The CLI will automatically extract it from the export. You do not need to pass it to `createWorker()`.\n */\nexport interface WorkerConfig {\n /**\n * Lambda function timeout in seconds (max 900).\n */\n timeout?: number;\n /**\n * Lambda function memory size in MB (128-10240).\n */\n memorySize?: number;\n /**\n * Optional Lambda layers ARNs to attach to this worker function.\n *\n * This is primarily used by @microfox/ai-worker-cli when generating serverless.yml.\n * Supports CloudFormation pseudo-parameters like ${aws:region} and ${aws:accountId}.\n *\n * Example:\n * layers: ['arn:aws:lambda:${aws:region}:${aws:accountId}:layer:ffmpeg:1']\n */\n layers?: string[];\n /**\n * Schedule events configuration for this worker.\n * Allows multiple schedule events to be attached to the same function.\n * \n * @example Simple rate\n * ```typescript\n * schedule: 'rate(2 hours)'\n * ```\n * \n * @example Multiple schedules\n * ```typescript\n * schedule: [\n * 'rate(2 hours)',\n * { rate: 'cron(0 12 * * ? *)', enabled: true, input: { key: 'value' } }\n * ]\n * ```\n * \n * @example Using scheduler method with timezone\n * ```typescript\n * schedule: {\n * method: 'scheduler',\n * rate: 'cron(0 0/4 ? * MON-FRI *)',\n * timezone: 'America/New_York',\n * input: { key1: 'value1' }\n * }\n * ```\n */\n schedule?: ScheduleConfig;\n\n /**\n * SQS queue settings for this worker (used by @microfox/ai-worker-cli when generating serverless.yml).\n *\n * Notes:\n * - To effectively disable retries, set `maxReceiveCount: 1` (requires DLQ; the CLI will create one).\n * - SQS does not support `maxReceiveCount: 0`.\n * - `messageRetentionPeriod` is in seconds (max 1209600 = 14 days).\n */\n sqs?: {\n /**\n * How many receives before sending to DLQ.\n * Use 1 to avoid retries.\n */\n maxReceiveCount?: number;\n /**\n * How long messages are retained in the main queue (seconds).\n */\n messageRetentionPeriod?: number;\n /**\n * Visibility timeout for the main queue (seconds).\n * If not set, CLI defaults to (worker timeout + 60s).\n */\n visibilityTimeout?: number;\n /**\n * DLQ message retention period (seconds).\n * Defaults to `messageRetentionPeriod` (or 14 days).\n */\n deadLetterMessageRetentionPeriod?: number;\n };\n}\n\nexport interface WorkerAgentConfig<INPUT_SCHEMA extends ZodType<any>, OUTPUT> {\n id: string;\n inputSchema: INPUT_SCHEMA;\n outputSchema: ZodType<OUTPUT>;\n handler: WorkerHandler<z.infer<INPUT_SCHEMA>, OUTPUT>;\n /**\n * @deprecated Prefer exporting `workerConfig` as a separate const from your worker file.\n * The CLI will automatically extract it from the export. This parameter is kept for backward compatibility.\n */\n workerConfig?: WorkerConfig;\n}\n\nexport interface WorkerAgent<INPUT_SCHEMA extends ZodType<any>, OUTPUT> {\n id: string;\n dispatch: (\n input: z.input<INPUT_SCHEMA>,\n options: DispatchOptions\n ) => Promise<DispatchResult>;\n handler: WorkerHandler<z.infer<INPUT_SCHEMA>, OUTPUT>;\n inputSchema: INPUT_SCHEMA;\n outputSchema: ZodType<OUTPUT>;\n workerConfig?: WorkerConfig;\n}\n\n/**\n * Creates a worker agent that can be dispatched to SQS/Lambda.\n *\n * In development mode (NODE_ENV === 'development' and WORKERS_LOCAL_MODE !== 'false'),\n * dispatch() will run the handler immediately in the same process.\n *\n * In production, dispatch() sends a message to SQS which triggers a Lambda function.\n *\n * @template INPUT_SCHEMA - The Zod schema type (e.g., `typeof InputSchema`).\n * Used to derive both:\n * - Pre-parse input type via `z.input<INPUT_SCHEMA>` for `dispatch()` (preserves optional fields)\n * - Parsed input type via `z.infer<INPUT_SCHEMA>` for handler (defaults applied)\n * @template OUTPUT - The output type returned by the handler. Use `z.infer<typeof OutputSchema>`.\n *\n * @param config - Worker agent configuration\n * @returns A worker agent object with a dispatch method\n *\n * @example\n * ```typescript\n * const InputSchema = z.object({\n * url: z.string().url(),\n * timeout: z.number().optional().default(5000), // optional with default\n * });\n *\n * export const worker = createWorker<typeof InputSchema, Output>({\n * // dispatch() accepts { url: string, timeout?: number } (pre-parse, optional preserved)\n * // handler receives { url: string, timeout: number } (parsed, default applied)\n * });\n * ```\n */\nexport function createWorker<INPUT_SCHEMA extends ZodType<any>, OUTPUT>(\n config: WorkerAgentConfig<INPUT_SCHEMA, OUTPUT>\n): WorkerAgent<INPUT_SCHEMA, OUTPUT> {\n const { id, inputSchema, outputSchema, handler } = config;\n\n const agent: WorkerAgent<INPUT_SCHEMA, OUTPUT> = {\n id,\n handler,\n inputSchema,\n outputSchema,\n\n async dispatch(input: z.input<INPUT_SCHEMA>, options: DispatchOptions): Promise<DispatchResult> {\n const mode = options.mode ?? 'auto';\n const envWantsLocal =\n process.env.NODE_ENV === 'development' &&\n process.env.WORKERS_LOCAL_MODE !== 'false';\n // Check if we're in local development mode\n const isLocal = mode === 'local' || (mode === 'auto' && envWantsLocal);\n\n if (isLocal) {\n // Local mode: run handler immediately\n // Parse input to apply defaults and get the final parsed type\n const parsedInput = inputSchema.parse(input);\n const localJobId = options.jobId || `local-${Date.now()}`;\n \n // Try to get direct job store access in local mode (same process as Next.js app)\n // This allows direct DB updates without needing HTTP/webhook URLs\n let directJobStore: {\n updateJob: (jobId: string, data: any) => Promise<void>;\n setJob?: (jobId: string, data: any) => Promise<void>;\n } | null = null;\n\n // Path constants for job store imports\n const nextJsPathAlias = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n\n // Reliable approach: try Next.js path alias first, then explicit env var\n // The @/ alias works at runtime in Next.js context\n const resolveJobStore = async () => {\n // Option 1: Try Next.js path alias (works in Next.js runtime context)\n try {\n const module = await import(nextJsPathAlias);\n if (module?.updateJob) {\n return { updateJob: module.updateJob, setJob: module.setJob };\n }\n } catch {\n // Path alias not available (not in Next.js context or alias not configured)\n }\n\n // Option 2: Use explicit env var if provided (for custom setups)\n if (explicitPath) {\n try {\n const module = await import(explicitPath).catch(() => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require(explicitPath);\n });\n if (module?.updateJob) {\n return { updateJob: module.updateJob, setJob: module.setJob };\n }\n } catch {\n // Explicit path failed\n }\n }\n\n return null;\n };\n\n directJobStore = await resolveJobStore();\n if (directJobStore) {\n console.log('[Worker] Using direct job store in local mode (no HTTP needed)');\n }\n\n // Derive job store URL from webhook URL or environment (fallback for HTTP mode)\n let jobStoreUrl: string | undefined;\n if (options.webhookUrl) {\n try {\n const webhookUrlObj = new URL(options.webhookUrl);\n jobStoreUrl = webhookUrlObj.pathname.replace(/\\/webhook$/, '');\n jobStoreUrl = `${webhookUrlObj.origin}${jobStoreUrl}`;\n } catch {\n // Invalid URL, skip job store URL\n }\n }\n jobStoreUrl = jobStoreUrl || process.env.WORKER_JOB_STORE_URL;\n\n // Create job store interface for local mode\n // Prefer direct DB access, fallback to HTTP calls if needed\n const createLocalJobStore = (\n directStore: typeof directJobStore,\n httpUrl?: string\n ): JobStore | undefined => {\n // If we have direct job store access, use it (no HTTP needed)\n if (directStore) {\n return {\n update: async (update) => {\n try {\n // Build update payload\n const updatePayload: any = {};\n \n if (update.status !== undefined) {\n updatePayload.status = update.status;\n }\n if (update.metadata !== undefined) {\n updatePayload.metadata = update.metadata;\n }\n if (update.progress !== undefined) {\n // Merge progress into metadata\n updatePayload.metadata = {\n ...updatePayload.metadata,\n progress: update.progress,\n progressMessage: update.progressMessage,\n };\n }\n if (update.output !== undefined) {\n updatePayload.output = update.output;\n }\n if (update.error !== undefined) {\n updatePayload.error = update.error;\n }\n\n await directStore.updateJob(localJobId, updatePayload);\n console.log('[Worker] Local job updated (direct DB):', {\n jobId: localJobId,\n workerId: id,\n updates: Object.keys(updatePayload),\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to update local job (direct DB):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n }\n },\n get: async () => {\n try {\n // Use the same direct store that has updateJob - it should also have getJob\n if (directStore) {\n // Try to import getJob from the same module\n const nextJsPath = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n \n for (const importPath of [nextJsPath, explicitPath].filter(Boolean)) {\n try {\n const module = await import(importPath!);\n if (module?.getJob) {\n return await module.getJob(localJobId);\n }\n } catch {\n // Continue\n }\n }\n }\n return null;\n } catch (error: any) {\n console.warn('[Worker] Failed to get local job (direct DB):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n return null;\n }\n },\n };\n }\n\n // Fallback to HTTP calls if no direct access\n if (!httpUrl) {\n return undefined;\n }\n\n // Use HTTP calls to update job store\n return {\n update: async (update) => {\n try {\n // Build update payload\n const updatePayload: any = { jobId: localJobId, workerId: id };\n \n if (update.status !== undefined) {\n updatePayload.status = update.status;\n }\n if (update.metadata !== undefined) {\n updatePayload.metadata = update.metadata;\n }\n if (update.progress !== undefined) {\n // Merge progress into metadata\n updatePayload.metadata = {\n ...updatePayload.metadata,\n progress: update.progress,\n progressMessage: update.progressMessage,\n };\n }\n if (update.output !== undefined) {\n updatePayload.output = update.output;\n }\n if (update.error !== undefined) {\n updatePayload.error = update.error;\n }\n\n const response = await fetch(`${httpUrl}/update`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(updatePayload),\n });\n if (!response.ok) {\n throw new Error(`Job store update failed: ${response.status} ${response.statusText}`);\n }\n console.log('[Worker] Local job updated (HTTP):', {\n jobId: localJobId,\n workerId: id,\n updates: Object.keys(updatePayload),\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to update local job (HTTP):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n }\n },\n get: async () => {\n try {\n // GET /api/workflows/workers/:workerId/:jobId\n const response = await fetch(`${httpUrl}/${id}/${localJobId}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Job store get failed: ${response.status} ${response.statusText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n console.warn('[Worker] Failed to get local job (HTTP):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n return null;\n }\n },\n };\n };\n\n const jobStore = createLocalJobStore(directJobStore, jobStoreUrl);\n\n // Create initial job record if we have job store access\n if (directJobStore?.setJob) {\n try {\n await directJobStore.setJob(localJobId, {\n jobId: localJobId,\n workerId: id,\n status: 'queued',\n input: parsedInput,\n metadata: options.metadata || {},\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to create initial job record:', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n // Continue - job will still be created when status is updated\n }\n }\n\n // Create handler context with job store\n const handlerContext = {\n jobId: localJobId,\n workerId: id,\n ...(jobStore ? { jobStore } : {}),\n };\n\n try {\n // Update status to running before execution\n if (jobStore) {\n await jobStore.update({ status: 'running' });\n }\n\n const output = await dispatchLocal(handler, parsedInput, handlerContext);\n\n // Update status to completed before webhook\n if (jobStore) {\n await jobStore.update({ status: 'completed', output });\n }\n\n // Only send webhook if webhookUrl is provided\n if (options.webhookUrl) {\n try {\n await fetch(options.webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jobId: localJobId,\n workerId: id,\n status: 'success',\n output,\n metadata: options.metadata,\n }),\n });\n } catch (error) {\n console.warn('[Worker] Local webhook call failed:', error);\n }\n }\n\n return {\n messageId: `local-${Date.now()}`,\n status: 'queued',\n jobId: localJobId,\n };\n } catch (error: any) {\n // Update status to failed before webhook\n if (jobStore) {\n await jobStore.update({\n status: 'failed',\n error: {\n message: error.message || 'Unknown error',\n stack: error.stack,\n name: error.name || 'Error',\n },\n });\n }\n\n // Only send error webhook if webhookUrl is provided\n if (options.webhookUrl) {\n try {\n await fetch(options.webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jobId: localJobId,\n workerId: id,\n status: 'error',\n error: {\n message: error.message || 'Unknown error',\n stack: error.stack,\n name: error.name || 'Error',\n },\n metadata: options.metadata,\n }),\n });\n } catch (webhookError) {\n console.warn('[Worker] Local error webhook call failed:', webhookError);\n }\n }\n throw error;\n }\n }\n\n // Production mode: dispatch to SQS\n return dispatch(id, input, inputSchema, options);\n },\n };\n\n return agent;\n}\n\n/**\n * Creates a Lambda handler entrypoint for a worker agent.\n * This is used by the deployment script to generate Lambda entrypoints.\n *\n * @param agent - The worker agent\n * @returns A Lambda handler function\n */\nexport function createLambdaEntrypoint<INPUT_SCHEMA extends ZodType<any>, OUTPUT>(\n agent: WorkerAgent<INPUT_SCHEMA, OUTPUT>\n) {\n return createLambdaHandler(agent.handler, agent.outputSchema);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAkPO,SAAS,aACd,QACmC;AACnC,QAAM,EAAE,IAAI,aAAa,cAAc,QAAQ,IAAI;AAEnD,QAAM,QAA2C;AAAA,IAC/C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IAEA,MAAM,SAAS,OAA8B,SAAmD;AAC9F,YAAM,OAAO,QAAQ,QAAQ;AAC7B,YAAM,gBACJ,QAAQ,IAAI,aAAa,iBACzB,QAAQ,IAAI,uBAAuB;AAErC,YAAM,UAAU,SAAS,WAAY,SAAS,UAAU;AAExD,UAAI,SAAS;AAGX,cAAM,cAAc,YAAY,MAAM,KAAK;AAC3C,cAAM,aAAa,QAAQ,SAAS,SAAS,KAAK,IAAI,CAAC;AAIvD,YAAI,iBAGO;AAGX,cAAM,kBAAkB;AACxB,cAAM,eAAe,QAAQ,IAAI;AAIjC,cAAM,kBAAkB,YAAY;AAElC,cAAI;AACF,kBAAM,SAAS,MAAM,OAAO;AAC5B,gBAAI,QAAQ,WAAW;AACrB,qBAAO,EAAE,WAAW,OAAO,WAAW,QAAQ,OAAO,OAAO;AAAA,YAC9D;AAAA,UACF,QAAQ;AAAA,UAER;AAGA,cAAI,cAAc;AAChB,gBAAI;AACF,oBAAM,SAAS,MAAM,OAAO,cAAc,MAAM,MAAM;AAEpD,uBAAO,UAAQ,YAAY;AAAA,cAC7B,CAAC;AACD,kBAAI,QAAQ,WAAW;AACrB,uBAAO,EAAE,WAAW,OAAO,WAAW,QAAQ,OAAO,OAAO;AAAA,cAC9D;AAAA,YACF,QAAQ;AAAA,YAER;AAAA,UACF;AAEA,iBAAO;AAAA,QACT;AAEA,yBAAiB,MAAM,gBAAgB;AACvC,YAAI,gBAAgB;AAClB,kBAAQ,IAAI,gEAAgE;AAAA,QAC9E;AAGA,YAAI;AACJ,YAAI,QAAQ,YAAY;AACtB,cAAI;AACF,kBAAM,gBAAgB,IAAI,IAAI,QAAQ,UAAU;AAChD,0BAAc,cAAc,SAAS,QAAQ,cAAc,EAAE;AAC7D,0BAAc,GAAG,cAAc,MAAM,GAAG,WAAW;AAAA,UACrD,QAAQ;AAAA,UAER;AAAA,QACF;AACA,sBAAc,eAAe,QAAQ,IAAI;AAIzC,cAAM,sBAAsB,CAC1B,aACA,YACyB;AAEzB,cAAI,aAAa;AACf,mBAAO;AAAA,cACL,QAAQ,OAAO,WAAW;AACxB,oBAAI;AAEF,wBAAM,gBAAqB,CAAC;AAE5B,sBAAI,OAAO,WAAW,QAAW;AAC/B,kCAAc,SAAS,OAAO;AAAA,kBAChC;AACA,sBAAI,OAAO,aAAa,QAAW;AACjC,kCAAc,WAAW,OAAO;AAAA,kBAClC;AACA,sBAAI,OAAO,aAAa,QAAW;AAEjC,kCAAc,WAAW;AAAA,sBACvB,GAAG,cAAc;AAAA,sBACjB,UAAU,OAAO;AAAA,sBACjB,iBAAiB,OAAO;AAAA,oBAC1B;AAAA,kBACF;AACA,sBAAI,OAAO,WAAW,QAAW;AAC/B,kCAAc,SAAS,OAAO;AAAA,kBAChC;AACA,sBAAI,OAAO,UAAU,QAAW;AAC9B,kCAAc,QAAQ,OAAO;AAAA,kBAC/B;AAEA,wBAAM,YAAY,UAAU,YAAY,aAAa;AACrD,0BAAQ,IAAI,2CAA2C;AAAA,oBACrD,OAAO;AAAA,oBACP,UAAU;AAAA,oBACV,SAAS,OAAO,KAAK,aAAa;AAAA,kBACpC,CAAC;AAAA,gBACH,SAAS,OAAY;AACnB,0BAAQ,KAAK,oDAAoD;AAAA,oBAC/D,OAAO;AAAA,oBACP,UAAU;AAAA,oBACV,OAAO,OAAO,WAAW,OAAO,KAAK;AAAA,kBACvC,CAAC;AAAA,gBACH;AAAA,cACF;AAAA,cACA,KAAK,YAAY;AACf,oBAAI;AAEF,sBAAI,aAAa;AAEf,0BAAM,aAAa;AACnB,0BAAMA,gBAAe,QAAQ,IAAI;AAEjC,+BAAW,cAAc,CAAC,YAAYA,aAAY,EAAE,OAAO,OAAO,GAAG;AACnE,0BAAI;AACF,8BAAM,SAAS,MAAM,OAAO;AAC5B,4BAAI,QAAQ,QAAQ;AAClB,iCAAO,MAAM,OAAO,OAAO,UAAU;AAAA,wBACvC;AAAA,sBACF,QAAQ;AAAA,sBAER;AAAA,oBACF;AAAA,kBACF;AACA,yBAAO;AAAA,gBACT,SAAS,OAAY;AACnB,0BAAQ,KAAK,iDAAiD;AAAA,oBAC5D,OAAO;AAAA,oBACP,UAAU;AAAA,oBACV,OAAO,OAAO,WAAW,OAAO,KAAK;AAAA,kBACvC,CAAC;AACD,yBAAO;AAAA,gBACT;AAAA,cACF;AAAA,YACF;AAAA,UACF;AAGA,cAAI,CAAC,SAAS;AACZ,mBAAO;AAAA,UACT;AAGA,iBAAO;AAAA,YACL,QAAQ,OAAO,WAAW;AACxB,kBAAI;AAEF,sBAAM,gBAAqB,EAAE,OAAO,YAAY,UAAU,GAAG;AAE7D,oBAAI,OAAO,WAAW,QAAW;AAC/B,gCAAc,SAAS,OAAO;AAAA,gBAChC;AACA,oBAAI,OAAO,aAAa,QAAW;AACjC,gCAAc,WAAW,OAAO;AAAA,gBAClC;AACA,oBAAI,OAAO,aAAa,QAAW;AAEjC,gCAAc,WAAW;AAAA,oBACvB,GAAG,cAAc;AAAA,oBACjB,UAAU,OAAO;AAAA,oBACjB,iBAAiB,OAAO;AAAA,kBAC1B;AAAA,gBACF;AACA,oBAAI,OAAO,WAAW,QAAW;AAC/B,gCAAc,SAAS,OAAO;AAAA,gBAChC;AACA,oBAAI,OAAO,UAAU,QAAW;AAC9B,gCAAc,QAAQ,OAAO;AAAA,gBAC/B;AAEA,sBAAM,WAAW,MAAM,MAAM,GAAG,OAAO,WAAW;AAAA,kBAChD,QAAQ;AAAA,kBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,kBAC9C,MAAM,KAAK,UAAU,aAAa;AAAA,gBACpC,CAAC;AACD,oBAAI,CAAC,SAAS,IAAI;AAChB,wBAAM,IAAI,MAAM,4BAA4B,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,gBACtF;AACA,wBAAQ,IAAI,sCAAsC;AAAA,kBAChD,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,SAAS,OAAO,KAAK,aAAa;AAAA,gBACpC,CAAC;AAAA,cACH,SAAS,OAAY;AACnB,wBAAQ,KAAK,+CAA+C;AAAA,kBAC1D,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,OAAO,OAAO,WAAW,OAAO,KAAK;AAAA,gBACvC,CAAC;AAAA,cACH;AAAA,YACF;AAAA,YACA,KAAK,YAAY;AACf,kBAAI;AAEF,sBAAM,WAAW,MAAM,MAAM,GAAG,OAAO,IAAI,EAAE,IAAI,UAAU,IAAI;AAAA,kBAC7D,QAAQ;AAAA,kBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,gBAChD,CAAC;AAED,oBAAI,CAAC,SAAS,IAAI;AAChB,sBAAI,SAAS,WAAW,KAAK;AAC3B,2BAAO;AAAA,kBACT;AACA,wBAAM,IAAI,MAAM,yBAAyB,SAAS,MAAM,IAAI,SAAS,UAAU,EAAE;AAAA,gBACnF;AAEA,uBAAO,MAAM,SAAS,KAAK;AAAA,cAC7B,SAAS,OAAY;AACnB,wBAAQ,KAAK,4CAA4C;AAAA,kBACvD,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,OAAO,OAAO,WAAW,OAAO,KAAK;AAAA,gBACvC,CAAC;AACD,uBAAO;AAAA,cACT;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAEA,cAAM,WAAW,oBAAoB,gBAAgB,WAAW;AAGhE,YAAI,gBAAgB,QAAQ;AAC1B,cAAI;AACF,kBAAM,eAAe,OAAO,YAAY;AAAA,cACtC,OAAO;AAAA,cACP,UAAU;AAAA,cACV,QAAQ;AAAA,cACR,OAAO;AAAA,cACP,UAAU,QAAQ,YAAY,CAAC;AAAA,YACjC,CAAC;AAAA,UACH,SAAS,OAAY;AACnB,oBAAQ,KAAK,iDAAiD;AAAA,cAC5D,OAAO;AAAA,cACP,UAAU;AAAA,cACV,OAAO,OAAO,WAAW,OAAO,KAAK;AAAA,YACvC,CAAC;AAAA,UAEH;AAAA,QACF;AAGA,cAAM,iBAAiB;AAAA,UACrB,OAAO;AAAA,UACP,UAAU;AAAA,UACV,GAAI,WAAW,EAAE,SAAS,IAAI,CAAC;AAAA,QACjC;AAEA,YAAI;AAEF,cAAI,UAAU;AACZ,kBAAM,SAAS,OAAO,EAAE,QAAQ,UAAU,CAAC;AAAA,UAC7C;AAEA,gBAAM,SAAS,MAAM,cAAc,SAAS,aAAa,cAAc;AAGvE,cAAI,UAAU;AACZ,kBAAM,SAAS,OAAO,EAAE,QAAQ,aAAa,OAAO,CAAC;AAAA,UACvD;AAGA,cAAI,QAAQ,YAAY;AACtB,gBAAI;AACF,oBAAM,MAAM,QAAQ,YAAY;AAAA,gBAC9B,QAAQ;AAAA,gBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,gBAC9C,MAAM,KAAK,UAAU;AAAA,kBACnB,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,QAAQ;AAAA,kBACR;AAAA,kBACA,UAAU,QAAQ;AAAA,gBACpB,CAAC;AAAA,cACH,CAAC;AAAA,YACH,SAAS,OAAO;AACd,sBAAQ,KAAK,uCAAuC,KAAK;AAAA,YAC3D;AAAA,UACF;AAEA,iBAAO;AAAA,YACL,WAAW,SAAS,KAAK,IAAI,CAAC;AAAA,YAC9B,QAAQ;AAAA,YACR,OAAO;AAAA,UACT;AAAA,QACF,SAAS,OAAY;AAEnB,cAAI,UAAU;AACZ,kBAAM,SAAS,OAAO;AAAA,cACpB,QAAQ;AAAA,cACR,OAAO;AAAA,gBACL,SAAS,MAAM,WAAW;AAAA,gBAC1B,OAAO,MAAM;AAAA,gBACb,MAAM,MAAM,QAAQ;AAAA,cACtB;AAAA,YACF,CAAC;AAAA,UACH;AAGA,cAAI,QAAQ,YAAY;AACtB,gBAAI;AACF,oBAAM,MAAM,QAAQ,YAAY;AAAA,gBAC9B,QAAQ;AAAA,gBACR,SAAS,EAAE,gBAAgB,mBAAmB;AAAA,gBAC9C,MAAM,KAAK,UAAU;AAAA,kBACnB,OAAO;AAAA,kBACP,UAAU;AAAA,kBACV,QAAQ;AAAA,kBACR,OAAO;AAAA,oBACL,SAAS,MAAM,WAAW;AAAA,oBAC1B,OAAO,MAAM;AAAA,oBACb,MAAM,MAAM,QAAQ;AAAA,kBACtB;AAAA,kBACA,UAAU,QAAQ;AAAA,gBACpB,CAAC;AAAA,cACH,CAAC;AAAA,YACH,SAAS,cAAc;AACrB,sBAAQ,KAAK,6CAA6C,YAAY;AAAA,YACxE;AAAA,UACF;AACA,gBAAM;AAAA,QACR;AAAA,MACF;AAGA,aAAO,SAAS,IAAI,OAAO,aAAa,OAAO;AAAA,IACjD;AAAA,EACF;AAEA,SAAO;AACT;AASO,SAAS,uBACd,OACA;AACA,SAAO,oBAAoB,MAAM,SAAS,MAAM,YAAY;AAC9D;","names":["explicitPath"]}
1
+ {"version":3,"sources":["../src/queue.ts","../src/index.ts"],"sourcesContent":["/**\n * Queue definition and context types for worker queues.\n *\n * These types are used at code-time by .queue.ts files and at runtime\n * by the client and generated registry/queue wrappers.\n */\n\nexport interface WorkerQueueStep {\n /** Worker ID for this step. Must match an existing worker id. */\n workerId: string;\n /**\n * Optional delay (in seconds) before this step is executed.\n * Implemented via SQS DelaySeconds (0–900).\n */\n delaySeconds?: number;\n /**\n * Optional name of a mapping function exported from the .queue.ts file\n * that derives this step's input from the previous step's output and\n * the original initial input.\n */\n mapInputFromPrev?: string;\n}\n\nexport interface WorkerQueueConfig<InitialInput = any, StepOutput = any> {\n /** Stable queue identifier, e.g. \"cost-usage\". */\n id: string;\n /** Ordered list of workers forming the queue. */\n steps: WorkerQueueStep[];\n /**\n * Optional schedule for the queue (cron or rate).\n * When set, the CLI generates a queue-starter Lambda triggered by this schedule.\n * Example: 'cron(0 3 * * ? *)' for daily at 03:00 UTC.\n */\n schedule?: string | { rate: string; enabled?: boolean; input?: Record<string, any> };\n // The generic parameters are reserved for future typing improvements and\n // are intentionally unused here (config is structural at runtime).\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _initialInputType?: InitialInput;\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _stepOutputType?: StepOutput;\n}\n\n/**\n * Queue execution context that is embedded into job input/metadata so\n * queue-aware wrappers can determine where they are in the queue.\n */\nexport interface WorkerQueueContext<InitialInput = any> {\n id: string;\n stepIndex: number;\n initialInput: InitialInput;\n /** Queue job ID (same as first worker's jobId) for tracking progress. */\n queueJobId?: string;\n}\n\n/**\n * Identity helper for defining worker queues in .queue.ts files.\n * This is primarily for type safety and CLI discovery.\n */\nexport function defineWorkerQueue<T extends WorkerQueueConfig>(config: T): T {\n return config;\n}\n\n","/**\n * @microfox/ai-worker\n * Worker runtime for ai-router - SQS-based async agent execution\n */\n\nimport { dispatch, dispatchLocal, getWorkersTriggerUrl, type DispatchOptions, type DispatchResult } from './client.js';\nimport { createLambdaHandler, type WorkerHandler, type JobStore, type DispatchWorkerOptions, SQS_MAX_DELAY_SECONDS } from './handler.js';\nimport type { ZodType, z } from 'zod';\n\nexport * from './client.js';\nexport * from './handler.js';\nexport * from './config.js';\nexport * from './queue.js';\n\n/**\n * Schedule event configuration for a worker.\n * Supports both simple rate/cron strings and full configuration objects.\n * \n * @example Simple rate/cron\n * ```typescript\n * schedule: 'rate(2 hours)'\n * // or\n * schedule: 'cron(0 12 * * ? *)'\n * ```\n * \n * @example Full configuration\n * ```typescript\n * schedule: {\n * rate: 'rate(10 minutes)',\n * enabled: true,\n * input: { key1: 'value1' }\n * }\n * ```\n * \n * @example Multiple schedules\n * ```typescript\n * schedule: [\n * 'rate(2 hours)',\n * { rate: 'cron(0 12 * * ? *)', enabled: false }\n * ]\n * ```\n */\nexport interface ScheduleEventConfig {\n /**\n * Schedule rate using either rate() or cron() syntax.\n * Can be a string or array of strings for multiple schedules.\n * \n * @example 'rate(2 hours)' or 'cron(0 12 * * ? *)'\n * @example ['cron(0 0/4 ? * MON-FRI *)', 'cron(0 2 ? * SAT-SUN *)']\n */\n rate: string | string[];\n /**\n * Whether the schedule is enabled (default: true).\n */\n enabled?: boolean;\n /**\n * Input payload to pass to the function.\n */\n input?: Record<string, any>;\n /**\n * JSONPath expression to select part of the event data as input.\n */\n inputPath?: string;\n /**\n * Input transformer configuration for custom input mapping.\n */\n inputTransformer?: {\n inputPathsMap?: Record<string, string>;\n inputTemplate?: string;\n };\n /**\n * Name of the schedule event.\n */\n name?: string;\n /**\n * Description of the schedule event.\n */\n description?: string;\n /**\n * Method to use: 'eventBus' (default) or 'scheduler'.\n * Use 'scheduler' for higher limits (1M events vs 300).\n */\n method?: 'eventBus' | 'scheduler';\n /**\n * Timezone for the schedule (only used with method: 'scheduler').\n * @example 'America/New_York'\n */\n timezone?: string;\n}\n\nexport type ScheduleConfig = \n | string \n | ScheduleEventConfig \n | (string | ScheduleEventConfig)[];\n\n/**\n * Configuration for a worker's Lambda function deployment.\n * \n * **Best Practice**: Export this as a separate const from your worker file:\n * ```typescript\n * export const workerConfig: WorkerConfig = {\n * timeout: 900,\n * memorySize: 2048,\n * layers: ['arn:aws:lambda:${aws:region}:${aws:accountId}:layer:ffmpeg:1'],\n * schedule: 'rate(2 hours)',\n * };\n * ```\n * \n * The CLI will automatically extract it from the export. You do not need to pass it to `createWorker()`.\n */\nexport interface WorkerConfig {\n /**\n * Lambda function timeout in seconds (max 900).\n */\n timeout?: number;\n /**\n * Lambda function memory size in MB (128-10240).\n */\n memorySize?: number;\n /**\n * Optional Lambda layers ARNs to attach to this worker function.\n *\n * This is primarily used by @microfox/ai-worker-cli when generating serverless.yml.\n * Supports CloudFormation pseudo-parameters like ${aws:region} and ${aws:accountId}.\n *\n * Example:\n * layers: ['arn:aws:lambda:${aws:region}:${aws:accountId}:layer:ffmpeg:1']\n */\n layers?: string[];\n /**\n * Schedule events configuration for this worker.\n * Allows multiple schedule events to be attached to the same function.\n * \n * @example Simple rate\n * ```typescript\n * schedule: 'rate(2 hours)'\n * ```\n * \n * @example Multiple schedules\n * ```typescript\n * schedule: [\n * 'rate(2 hours)',\n * { rate: 'cron(0 12 * * ? *)', enabled: true, input: { key: 'value' } }\n * ]\n * ```\n * \n * @example Using scheduler method with timezone\n * ```typescript\n * schedule: {\n * method: 'scheduler',\n * rate: 'cron(0 0/4 ? * MON-FRI *)',\n * timezone: 'America/New_York',\n * input: { key1: 'value1' }\n * }\n * ```\n */\n schedule?: ScheduleConfig;\n\n /**\n * SQS queue settings for this worker (used by @microfox/ai-worker-cli when generating serverless.yml).\n *\n * Notes:\n * - To effectively disable retries, set `maxReceiveCount: 1` (requires DLQ; the CLI will create one).\n * - SQS does not support `maxReceiveCount: 0`.\n * - `messageRetentionPeriod` is in seconds (max 1209600 = 14 days).\n */\n sqs?: {\n /**\n * How many receives before sending to DLQ.\n * Use 1 to avoid retries.\n */\n maxReceiveCount?: number;\n /**\n * How long messages are retained in the main queue (seconds).\n */\n messageRetentionPeriod?: number;\n /**\n * Visibility timeout for the main queue (seconds).\n * If not set, CLI defaults to (worker timeout + 60s).\n */\n visibilityTimeout?: number;\n /**\n * DLQ message retention period (seconds).\n * Defaults to `messageRetentionPeriod` (or 14 days).\n */\n deadLetterMessageRetentionPeriod?: number;\n };\n}\n\nexport interface WorkerAgentConfig<INPUT_SCHEMA extends ZodType<any>, OUTPUT> {\n id: string;\n inputSchema: INPUT_SCHEMA;\n outputSchema: ZodType<OUTPUT>;\n handler: WorkerHandler<z.infer<INPUT_SCHEMA>, OUTPUT>;\n /**\n * @deprecated Prefer exporting `workerConfig` as a separate const from your worker file.\n * The CLI will automatically extract it from the export. This parameter is kept for backward compatibility.\n */\n workerConfig?: WorkerConfig;\n}\n\nexport interface WorkerAgent<INPUT_SCHEMA extends ZodType<any>, OUTPUT> {\n id: string;\n dispatch: (\n input: z.input<INPUT_SCHEMA>,\n options: DispatchOptions\n ) => Promise<DispatchResult>;\n handler: WorkerHandler<z.infer<INPUT_SCHEMA>, OUTPUT>;\n inputSchema: INPUT_SCHEMA;\n outputSchema: ZodType<OUTPUT>;\n workerConfig?: WorkerConfig;\n}\n\n/**\n * Creates a worker agent that can be dispatched to SQS/Lambda.\n *\n * In development mode (NODE_ENV === 'development' and WORKERS_LOCAL_MODE !== 'false'),\n * dispatch() will run the handler immediately in the same process.\n *\n * In production, dispatch() sends a message to SQS which triggers a Lambda function.\n *\n * @template INPUT_SCHEMA - The Zod schema type (e.g., `typeof InputSchema`).\n * Used to derive both:\n * - Pre-parse input type via `z.input<INPUT_SCHEMA>` for `dispatch()` (preserves optional fields)\n * - Parsed input type via `z.infer<INPUT_SCHEMA>` for handler (defaults applied)\n * @template OUTPUT - The output type returned by the handler. Use `z.infer<typeof OutputSchema>`.\n *\n * @param config - Worker agent configuration\n * @returns A worker agent object with a dispatch method\n *\n * @example\n * ```typescript\n * const InputSchema = z.object({\n * url: z.string().url(),\n * timeout: z.number().optional().default(5000), // optional with default\n * });\n *\n * export const worker = createWorker<typeof InputSchema, Output>({\n * // dispatch() accepts { url: string, timeout?: number } (pre-parse, optional preserved)\n * // handler receives { url: string, timeout: number } (parsed, default applied)\n * });\n * ```\n */\nexport function createWorker<INPUT_SCHEMA extends ZodType<any>, OUTPUT>(\n config: WorkerAgentConfig<INPUT_SCHEMA, OUTPUT>\n): WorkerAgent<INPUT_SCHEMA, OUTPUT> {\n const { id, inputSchema, outputSchema, handler } = config;\n\n const agent: WorkerAgent<INPUT_SCHEMA, OUTPUT> = {\n id,\n handler,\n inputSchema,\n outputSchema,\n\n async dispatch(input: z.input<INPUT_SCHEMA>, options: DispatchOptions): Promise<DispatchResult> {\n const mode = options.mode ?? 'auto';\n const envWantsLocal =\n process.env.NODE_ENV === 'development' &&\n process.env.WORKERS_LOCAL_MODE !== 'false';\n // Check if we're in local development mode\n const isLocal = mode === 'local' || (mode === 'auto' && envWantsLocal);\n\n if (isLocal) {\n // Local mode: run handler immediately\n // Parse input to apply defaults and get the final parsed type\n const parsedInput = inputSchema.parse(input);\n const localJobId = options.jobId || `local-${Date.now()}`;\n \n // Try to get direct job store access in local mode (same process as Next.js app)\n // This allows direct DB updates without needing HTTP/webhook URLs\n let directJobStore: {\n updateJob: (jobId: string, data: any) => Promise<void>;\n setJob?: (jobId: string, data: any) => Promise<void>;\n } | null = null;\n\n // Path constants for job store imports\n const nextJsPathAlias = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n\n // Reliable approach: try Next.js path alias first, then explicit env var\n // The @/ alias works at runtime in Next.js context\n const resolveJobStore = async () => {\n // Option 1: Try Next.js path alias (works in Next.js runtime context)\n try {\n const module = await import(nextJsPathAlias);\n if (module?.updateJob) {\n return { updateJob: module.updateJob, setJob: module.setJob };\n }\n } catch {\n // Path alias not available (not in Next.js context or alias not configured)\n }\n\n // Option 2: Use explicit env var if provided (for custom setups)\n if (explicitPath) {\n try {\n const module = await import(explicitPath).catch(() => {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n return require(explicitPath);\n });\n if (module?.updateJob) {\n return { updateJob: module.updateJob, setJob: module.setJob };\n }\n } catch {\n // Explicit path failed\n }\n }\n\n return null;\n };\n\n directJobStore = await resolveJobStore();\n if (directJobStore) {\n console.log('[Worker] Using direct job store in local mode (no HTTP needed)');\n }\n\n // Derive job store URL from webhook URL or environment (fallback for HTTP mode)\n let jobStoreUrl: string | undefined;\n if (options.webhookUrl) {\n try {\n const webhookUrlObj = new URL(options.webhookUrl);\n jobStoreUrl = webhookUrlObj.pathname.replace(/\\/webhook$/, '');\n jobStoreUrl = `${webhookUrlObj.origin}${jobStoreUrl}`;\n } catch {\n // Invalid URL, skip job store URL\n }\n }\n jobStoreUrl = jobStoreUrl || process.env.WORKER_JOB_STORE_URL;\n\n // Create job store interface for local mode\n // Prefer direct DB access, fallback to HTTP calls if needed\n const createLocalJobStore = (\n directStore: typeof directJobStore,\n httpUrl?: string\n ): JobStore | undefined => {\n // If we have direct job store access, use it (no HTTP needed)\n if (directStore) {\n return {\n update: async (update) => {\n try {\n // Build update payload\n const updatePayload: any = {};\n \n if (update.status !== undefined) {\n updatePayload.status = update.status;\n }\n if (update.metadata !== undefined) {\n updatePayload.metadata = update.metadata;\n }\n if (update.progress !== undefined) {\n // Merge progress into metadata\n updatePayload.metadata = {\n ...updatePayload.metadata,\n progress: update.progress,\n progressMessage: update.progressMessage,\n };\n }\n if (update.output !== undefined) {\n updatePayload.output = update.output;\n }\n if (update.error !== undefined) {\n updatePayload.error = update.error;\n }\n\n await directStore.updateJob(localJobId, updatePayload);\n console.log('[Worker] Local job updated (direct DB):', {\n jobId: localJobId,\n workerId: id,\n updates: Object.keys(updatePayload),\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to update local job (direct DB):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n }\n },\n get: async () => {\n try {\n // Use the same direct store that has updateJob - it should also have getJob\n if (directStore) {\n // Try to import getJob from the same module\n const nextJsPath = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n \n for (const importPath of [nextJsPath, explicitPath].filter(Boolean)) {\n try {\n const module = await import(importPath!);\n if (module?.getJob) {\n return await module.getJob(localJobId);\n }\n } catch {\n // Continue\n }\n }\n }\n return null;\n } catch (error: any) {\n console.warn('[Worker] Failed to get local job (direct DB):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n return null;\n }\n },\n appendInternalJob: async (entry: { jobId: string; workerId: string }) => {\n try {\n const nextJsPath = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n for (const importPath of [nextJsPath, explicitPath].filter(Boolean)) {\n try {\n const module = await import(importPath!);\n if (typeof module?.appendInternalJob === 'function') {\n await module.appendInternalJob(localJobId, entry);\n return;\n }\n } catch {\n // Continue\n }\n }\n } catch (error: any) {\n console.warn('[Worker] Failed to appendInternalJob (direct DB):', { localJobId, error: error?.message || String(error) });\n }\n },\n getJob: async (otherJobId: string) => {\n try {\n const nextJsPath = '@/app/api/workflows/stores/jobStore';\n const explicitPath = process.env.WORKER_JOB_STORE_MODULE_PATH;\n for (const importPath of [nextJsPath, explicitPath].filter(Boolean)) {\n try {\n const module = await import(importPath!);\n if (typeof module?.getJob === 'function') {\n return await module.getJob(otherJobId);\n }\n } catch {\n // Continue\n }\n }\n } catch (error: any) {\n console.warn('[Worker] Failed to getJob (direct DB):', { otherJobId, error: error?.message || String(error) });\n }\n return null;\n },\n };\n }\n\n // Fallback to HTTP calls if no direct access\n if (!httpUrl) {\n return undefined;\n }\n\n // Use HTTP calls to update job store\n return {\n update: async (update) => {\n try {\n // Build update payload\n const updatePayload: any = { jobId: localJobId, workerId: id };\n \n if (update.status !== undefined) {\n updatePayload.status = update.status;\n }\n if (update.metadata !== undefined) {\n updatePayload.metadata = update.metadata;\n }\n if (update.progress !== undefined) {\n // Merge progress into metadata\n updatePayload.metadata = {\n ...updatePayload.metadata,\n progress: update.progress,\n progressMessage: update.progressMessage,\n };\n }\n if (update.output !== undefined) {\n updatePayload.output = update.output;\n }\n if (update.error !== undefined) {\n updatePayload.error = update.error;\n }\n\n const response = await fetch(`${httpUrl}/update`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(updatePayload),\n });\n if (!response.ok) {\n throw new Error(`Job store update failed: ${response.status} ${response.statusText}`);\n }\n console.log('[Worker] Local job updated (HTTP):', {\n jobId: localJobId,\n workerId: id,\n updates: Object.keys(updatePayload),\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to update local job (HTTP):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n }\n },\n get: async () => {\n try {\n // GET /api/workflows/workers/:workerId/:jobId\n const response = await fetch(`${httpUrl}/${id}/${localJobId}`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n if (response.status === 404) {\n return null;\n }\n throw new Error(`Job store get failed: ${response.status} ${response.statusText}`);\n }\n\n return await response.json();\n } catch (error: any) {\n console.warn('[Worker] Failed to get local job (HTTP):', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n return null;\n }\n },\n };\n };\n\n const jobStore = createLocalJobStore(directJobStore, jobStoreUrl);\n\n const DEFAULT_POLL_INTERVAL_MS = 2000;\n const DEFAULT_POLL_TIMEOUT_MS = 15 * 60 * 1000;\n\n const createLocalDispatchWorker = (\n parentJobId: string,\n parentWorkerId: string,\n parentContext: Record<string, any>,\n store: JobStore | undefined\n ): ((\n workerId: string,\n input: unknown,\n options?: DispatchWorkerOptions\n ) => Promise<{ jobId: string; messageId?: string; output?: unknown }>) => {\n return async (\n calleeWorkerId: string,\n input: unknown,\n options?: DispatchWorkerOptions\n ): Promise<{ jobId: string; messageId?: string; output?: unknown }> => {\n const childJobId =\n options?.jobId ||\n `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;\n const metadata = options?.metadata ?? {};\n const serializedContext: Record<string, any> = {};\n if (parentContext.requestId) serializedContext.requestId = parentContext.requestId;\n const messageBody = {\n workerId: calleeWorkerId,\n jobId: childJobId,\n input: input ?? {},\n context: serializedContext,\n webhookUrl: options?.webhookUrl,\n metadata,\n timestamp: new Date().toISOString(),\n };\n let triggerUrl: string;\n try {\n triggerUrl = getWorkersTriggerUrl();\n } catch (e: any) {\n throw new Error(\n `Local dispatchWorker requires WORKER_BASE_URL (or similar) for worker \"${calleeWorkerId}\": ${e?.message ?? e}`\n );\n }\n const headers: Record<string, string> = { 'Content-Type': 'application/json' };\n const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;\n if (triggerKey) headers['x-workers-trigger-key'] = triggerKey;\n\n // Fire-and-forget with delay: schedule trigger after delay, return immediately (no computation/wait in caller).\n if (options?.await !== true && options?.delaySeconds != null && options.delaySeconds > 0) {\n const sec = Math.min(SQS_MAX_DELAY_SECONDS, Math.max(0, Math.floor(options.delaySeconds)));\n const storeRef = store;\n setTimeout(() => {\n fetch(triggerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody }),\n })\n .then(async (response) => {\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n console.error(\n `[Worker] Delayed trigger failed for \"${calleeWorkerId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n return;\n }\n if (storeRef?.appendInternalJob) {\n await storeRef.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });\n }\n })\n .catch((err) => {\n console.error('[Worker] Delayed trigger error:', { calleeWorkerId, jobId: childJobId, error: err?.message ?? err });\n });\n }, sec * 1000);\n return { jobId: childJobId, messageId: undefined };\n }\n\n const response = await fetch(triggerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({ workerId: calleeWorkerId, body: messageBody }),\n });\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to trigger worker \"${calleeWorkerId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n }\n const data = (await response.json().catch(() => ({}))) as any;\n const messageId = data?.messageId ? String(data.messageId) : `trigger-${childJobId}`;\n\n if (store?.appendInternalJob) {\n await store.appendInternalJob({ jobId: childJobId, workerId: calleeWorkerId });\n }\n\n if (options?.await && store?.getJob) {\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;\n const pollTimeoutMs = options.pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS;\n const deadline = Date.now() + pollTimeoutMs;\n while (Date.now() < deadline) {\n const child = await store.getJob(childJobId);\n if (!child) {\n await new Promise((r) => setTimeout(r, pollIntervalMs));\n continue;\n }\n if (child.status === 'completed') {\n return { jobId: childJobId, messageId, output: child.output };\n }\n if (child.status === 'failed') {\n const err = child.error;\n throw new Error(err?.message ?? `Child worker ${calleeWorkerId} failed`);\n }\n await new Promise((r) => setTimeout(r, pollIntervalMs));\n }\n throw new Error(\n `Child worker ${calleeWorkerId} (${childJobId}) did not complete within ${pollTimeoutMs}ms`\n );\n }\n\n return { jobId: childJobId, messageId };\n };\n };\n\n // Create initial job record if we have job store access\n if (directJobStore?.setJob) {\n try {\n await directJobStore.setJob(localJobId, {\n jobId: localJobId,\n workerId: id,\n status: 'queued',\n input: parsedInput,\n metadata: options.metadata || {},\n });\n } catch (error: any) {\n console.warn('[Worker] Failed to create initial job record:', {\n jobId: localJobId,\n workerId: id,\n error: error?.message || String(error),\n });\n // Continue - job will still be created when status is updated\n }\n }\n\n const baseContext = { jobId: localJobId, workerId: id };\n const handlerContext = {\n ...baseContext,\n ...(jobStore ? { jobStore } : {}),\n dispatchWorker: createLocalDispatchWorker(\n localJobId,\n id,\n baseContext,\n jobStore\n ),\n };\n\n try {\n // Update status to running before execution\n if (jobStore) {\n await jobStore.update({ status: 'running' });\n }\n\n const output = await dispatchLocal(handler, parsedInput, handlerContext);\n\n // Update status to completed before webhook\n if (jobStore) {\n await jobStore.update({ status: 'completed', output });\n }\n\n // Only send webhook if webhookUrl is provided\n if (options.webhookUrl) {\n try {\n await fetch(options.webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jobId: localJobId,\n workerId: id,\n status: 'success',\n output,\n metadata: options.metadata,\n }),\n });\n } catch (error) {\n console.warn('[Worker] Local webhook call failed:', error);\n }\n }\n\n return {\n messageId: `local-${Date.now()}`,\n status: 'queued',\n jobId: localJobId,\n };\n } catch (error: any) {\n // Update status to failed before webhook\n if (jobStore) {\n await jobStore.update({\n status: 'failed',\n error: {\n message: error.message || 'Unknown error',\n stack: error.stack,\n name: error.name || 'Error',\n },\n });\n }\n\n // Only send error webhook if webhookUrl is provided\n if (options.webhookUrl) {\n try {\n await fetch(options.webhookUrl, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n jobId: localJobId,\n workerId: id,\n status: 'error',\n error: {\n message: error.message || 'Unknown error',\n stack: error.stack,\n name: error.name || 'Error',\n },\n metadata: options.metadata,\n }),\n });\n } catch (webhookError) {\n console.warn('[Worker] Local error webhook call failed:', webhookError);\n }\n }\n throw error;\n }\n }\n\n // Production mode: dispatch to SQS\n return dispatch(id, input, inputSchema, options);\n },\n };\n\n return agent;\n}\n\n/**\n * Creates a Lambda handler entrypoint for a worker agent.\n * This is used by the deployment script to generate Lambda entrypoints.\n *\n * @param agent - The worker agent\n * @returns A Lambda handler function\n */\nexport function createLambdaEntrypoint<INPUT_SCHEMA extends ZodType<any>, OUTPUT>(\n agent: WorkerAgent<INPUT_SCHEMA, OUTPUT>\n) {\n return createLambdaHandler(agent.handler, agent.outputSchema);\n}\n"],"mappings":"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","names":["explicitPath","input","options","response"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@microfox/ai-worker",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "Background worker runtime for ai-router - SQS-based async agent execution",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -42,6 +42,8 @@
42
42
  }
43
43
  },
44
44
  "dependencies": {
45
+ "@aws-sdk/client-sqs": "^3.700.0",
46
+ "@upstash/redis": "^1.29.0",
45
47
  "mongodb": "^6.12.0",
46
48
  "zod": "^4.1.5"
47
49
  },
@@ -1,83 +0,0 @@
1
- // src/client.ts
2
- function getWorkersTriggerUrl() {
3
- const raw = process.env.WORKER_BASE_URL || process.env.NEXT_PUBLIC_WORKER_BASE_URL || process.env.WORKERS_TRIGGER_API_URL || process.env.NEXT_PUBLIC_WORKERS_TRIGGER_API_URL || process.env.WORKERS_CONFIG_API_URL || process.env.NEXT_PUBLIC_WORKERS_CONFIG_API_URL;
4
- if (!raw) {
5
- throw new Error(
6
- "WORKER_BASE_URL (preferred) or NEXT_PUBLIC_WORKER_BASE_URL is required for background workers"
7
- );
8
- }
9
- const url = new URL(raw);
10
- url.search = "";
11
- url.hash = "";
12
- const path = url.pathname || "";
13
- url.pathname = path.replace(/\/?workers\/(trigger|config)\/?$/, "");
14
- const basePath = url.pathname.replace(/\/+$/, "");
15
- url.pathname = `${basePath}/workers/trigger`.replace(/\/+$/, "");
16
- return url.toString();
17
- }
18
- function serializeContext(ctx) {
19
- const serialized = {};
20
- if (ctx.requestId) {
21
- serialized.requestId = ctx.requestId;
22
- }
23
- if (ctx.metadata && typeof ctx.metadata === "object") {
24
- Object.assign(serialized, ctx.metadata);
25
- }
26
- if (ctx._serializeContext && typeof ctx._serializeContext === "function") {
27
- const custom = ctx._serializeContext();
28
- Object.assign(serialized, custom);
29
- }
30
- return serialized;
31
- }
32
- async function dispatch(workerId, input, inputSchema, options, ctx) {
33
- const validatedInput = inputSchema.parse(input);
34
- const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
35
- const triggerUrl = getWorkersTriggerUrl();
36
- const serializedContext = ctx ? serializeContext(ctx) : {};
37
- const messageBody = {
38
- workerId,
39
- jobId,
40
- input: validatedInput,
41
- context: serializedContext,
42
- webhookUrl: options.webhookUrl,
43
- metadata: options.metadata || {},
44
- timestamp: (/* @__PURE__ */ new Date()).toISOString()
45
- };
46
- const headers = {
47
- "Content-Type": "application/json"
48
- };
49
- const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
50
- if (triggerKey) {
51
- headers["x-workers-trigger-key"] = triggerKey;
52
- }
53
- const response = await fetch(triggerUrl, {
54
- method: "POST",
55
- headers,
56
- body: JSON.stringify({
57
- workerId,
58
- body: messageBody
59
- })
60
- });
61
- if (!response.ok) {
62
- const text = await response.text().catch(() => "");
63
- throw new Error(
64
- `Failed to trigger worker "${workerId}": ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
65
- );
66
- }
67
- const data = await response.json().catch(() => ({}));
68
- const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;
69
- return {
70
- messageId,
71
- status: "queued",
72
- jobId
73
- };
74
- }
75
- async function dispatchLocal(handler, input, ctx) {
76
- return handler({ input, ctx: ctx || {} });
77
- }
78
-
79
- export {
80
- dispatch,
81
- dispatchLocal
82
- };
83
- //# sourceMappingURL=chunk-FQCZSXDI.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * Client for dispatching background worker jobs.\n *\n * In production, dispatching happens via the workers HTTP API:\n * POST /workers/trigger -> enqueues message to SQS on the workers service side\n *\n * This avoids requiring AWS credentials in your Next.js app.\n */\n\nimport type { ZodType, z } from 'zod';\n\nexport interface DispatchOptions {\n /**\n * Optional webhook callback URL to notify when the job finishes.\n * Only called when provided. Default: no webhook (use job store / MongoDB only).\n */\n webhookUrl?: string;\n /**\n * Controls how dispatch executes.\n * - \"auto\" (default): local inline execution in development unless WORKERS_LOCAL_MODE=false.\n * - \"local\": force inline execution (no SQS).\n * - \"remote\": force SQS/Lambda dispatch even in development.\n */\n mode?: 'auto' | 'local' | 'remote';\n jobId?: string;\n metadata?: Record<string, any>;\n}\n\nexport interface DispatchResult {\n messageId: string;\n status: 'queued';\n jobId: string;\n}\n\nexport interface SerializedContext {\n requestId?: string;\n userId?: string;\n traceId?: string;\n [key: string]: any;\n}\n\n/**\n * Derives the full /workers/trigger URL from env.\n *\n * Preferred env vars:\n * - WORKER_BASE_URL: base URL of the workers service (e.g. https://.../prod)\n * - NEXT_PUBLIC_WORKER_BASE_URL: same, but exposed to the browser\n *\n * Legacy env vars (still supported for backwards compatibility):\n * - WORKERS_TRIGGER_API_URL / NEXT_PUBLIC_WORKERS_TRIGGER_API_URL\n * - WORKERS_CONFIG_API_URL / NEXT_PUBLIC_WORKERS_CONFIG_API_URL\n */\nfunction getWorkersTriggerUrl(): string {\n const raw =\n process.env.WORKER_BASE_URL ||\n process.env.NEXT_PUBLIC_WORKER_BASE_URL ||\n process.env.WORKERS_TRIGGER_API_URL ||\n process.env.NEXT_PUBLIC_WORKERS_TRIGGER_API_URL ||\n process.env.WORKERS_CONFIG_API_URL ||\n process.env.NEXT_PUBLIC_WORKERS_CONFIG_API_URL;\n\n if (!raw) {\n throw new Error(\n 'WORKER_BASE_URL (preferred) or NEXT_PUBLIC_WORKER_BASE_URL is required for background workers'\n );\n }\n\n const url = new URL(raw);\n url.search = '';\n url.hash = '';\n\n const path = url.pathname || '';\n\n // If the user pointed at a specific endpoint, normalize back to the service root.\n url.pathname = path.replace(/\\/?workers\\/(trigger|config)\\/?$/, '');\n\n const basePath = url.pathname.replace(/\\/+$/, '');\n url.pathname = `${basePath}/workers/trigger`.replace(/\\/+$/, '');\n\n return url.toString();\n}\n\n/**\n * Serializes context data for transmission to Lambda.\n * Only serializes safe, JSON-compatible properties.\n */\nfunction serializeContext(ctx: any): SerializedContext {\n const serialized: SerializedContext = {};\n\n if (ctx.requestId) {\n serialized.requestId = ctx.requestId;\n }\n\n // Extract any additional serializable metadata\n if (ctx.metadata && typeof ctx.metadata === 'object') {\n Object.assign(serialized, ctx.metadata);\n }\n\n // Allow custom context serialization via a helper property\n if (ctx._serializeContext && typeof ctx._serializeContext === 'function') {\n const custom = ctx._serializeContext();\n Object.assign(serialized, custom);\n }\n\n return serialized;\n}\n\n/**\n * Dispatches a background worker job to SQS.\n *\n * @param workerId - The ID of the worker to dispatch\n * @param input - The input data for the worker (will be validated against inputSchema)\n * @param inputSchema - Zod schema for input validation\n * @param options - Dispatch options including webhook URL\n * @param ctx - Optional context object (only serializable parts will be sent)\n * @returns Promise resolving to dispatch result with messageId and jobId\n */\nexport async function dispatch<INPUT_SCHEMA extends ZodType<any>>(\n workerId: string,\n input: z.input<INPUT_SCHEMA>,\n inputSchema: INPUT_SCHEMA,\n options: DispatchOptions,\n ctx?: any\n): Promise<DispatchResult> {\n // Validate input against schema\n const validatedInput = inputSchema.parse(input);\n\n // Generate job ID if not provided\n const jobId =\n options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;\n\n // Resolve /workers/trigger endpoint URL\n const triggerUrl = getWorkersTriggerUrl();\n\n // Serialize context (only safe, JSON-compatible parts)\n const serializedContext = ctx ? serializeContext(ctx) : {};\n\n // Job updates use MongoDB only; never pass jobStoreUrl/origin URL.\n const messageBody = {\n workerId,\n jobId,\n input: validatedInput,\n context: serializedContext,\n webhookUrl: options.webhookUrl,\n metadata: options.metadata || {},\n timestamp: new Date().toISOString(),\n };\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n };\n const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;\n if (triggerKey) {\n headers['x-workers-trigger-key'] = triggerKey;\n }\n\n const response = await fetch(triggerUrl, {\n method: 'POST',\n headers,\n body: JSON.stringify({\n workerId,\n body: messageBody,\n }),\n });\n\n if (!response.ok) {\n const text = await response.text().catch(() => '');\n throw new Error(\n `Failed to trigger worker \"${workerId}\": ${response.status} ${response.statusText}${text ? ` - ${text}` : ''}`\n );\n }\n\n const data = (await response.json().catch(() => ({}))) as any;\n const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;\n\n return {\n messageId,\n status: 'queued',\n jobId,\n };\n}\n\n/**\n * Local development mode: runs the handler immediately in the same process.\n * This bypasses SQS and Lambda for faster iteration during development.\n *\n * @param handler - The worker handler function\n * @param input - The input data\n * @param ctx - The context object\n * @returns The handler result\n */\nexport async function dispatchLocal<INPUT, OUTPUT>(\n handler: (params: { input: INPUT; ctx: any }) => Promise<OUTPUT>,\n input: INPUT,\n ctx?: any\n): Promise<OUTPUT> {\n return handler({ input, ctx: ctx || {} });\n}\n"],"mappings":";AAoDA,SAAS,uBAA+B;AACtC,QAAM,MACJ,QAAQ,IAAI,mBACZ,QAAQ,IAAI,+BACZ,QAAQ,IAAI,2BACZ,QAAQ,IAAI,uCACZ,QAAQ,IAAI,0BACZ,QAAQ,IAAI;AAEd,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM,IAAI,IAAI,GAAG;AACvB,MAAI,SAAS;AACb,MAAI,OAAO;AAEX,QAAM,OAAO,IAAI,YAAY;AAG7B,MAAI,WAAW,KAAK,QAAQ,oCAAoC,EAAE;AAElE,QAAM,WAAW,IAAI,SAAS,QAAQ,QAAQ,EAAE;AAChD,MAAI,WAAW,GAAG,QAAQ,mBAAmB,QAAQ,QAAQ,EAAE;AAE/D,SAAO,IAAI,SAAS;AACtB;AAMA,SAAS,iBAAiB,KAA6B;AACrD,QAAM,aAAgC,CAAC;AAEvC,MAAI,IAAI,WAAW;AACjB,eAAW,YAAY,IAAI;AAAA,EAC7B;AAGA,MAAI,IAAI,YAAY,OAAO,IAAI,aAAa,UAAU;AACpD,WAAO,OAAO,YAAY,IAAI,QAAQ;AAAA,EACxC;AAGA,MAAI,IAAI,qBAAqB,OAAO,IAAI,sBAAsB,YAAY;AACxE,UAAM,SAAS,IAAI,kBAAkB;AACrC,WAAO,OAAO,YAAY,MAAM;AAAA,EAClC;AAEA,SAAO;AACT;AAYA,eAAsB,SACpB,UACA,OACA,aACA,SACA,KACyB;AAEzB,QAAM,iBAAiB,YAAY,MAAM,KAAK;AAG9C,QAAM,QACJ,QAAQ,SAAS,OAAO,KAAK,IAAI,CAAC,IAAI,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,GAAG,CAAC,CAAC;AAG/E,QAAM,aAAa,qBAAqB;AAGxC,QAAM,oBAAoB,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAGzD,QAAM,cAAc;AAAA,IAClB;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,SAAS;AAAA,IACT,YAAY,QAAQ;AAAA,IACpB,UAAU,QAAQ,YAAY,CAAC;AAAA,IAC/B,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AAEA,QAAM,UAAkC;AAAA,IACtC,gBAAgB;AAAA,EAClB;AACA,QAAM,aAAa,QAAQ,IAAI;AAC/B,MAAI,YAAY;AACd,YAAQ,uBAAuB,IAAI;AAAA,EACrC;AAEA,QAAM,WAAW,MAAM,MAAM,YAAY;AAAA,IACvC,QAAQ;AAAA,IACR;AAAA,IACA,MAAM,KAAK,UAAU;AAAA,MACnB;AAAA,MACA,MAAM;AAAA,IACR,CAAC;AAAA,EACH,CAAC;AAED,MAAI,CAAC,SAAS,IAAI;AAChB,UAAM,OAAO,MAAM,SAAS,KAAK,EAAE,MAAM,MAAM,EAAE;AACjD,UAAM,IAAI;AAAA,MACR,6BAA6B,QAAQ,MAAM,SAAS,MAAM,IAAI,SAAS,UAAU,GAAG,OAAO,MAAM,IAAI,KAAK,EAAE;AAAA,IAC9G;AAAA,EACF;AAEA,QAAM,OAAQ,MAAM,SAAS,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AACpD,QAAM,YAAY,MAAM,YAAY,OAAO,KAAK,SAAS,IAAI,WAAW,KAAK;AAE7E,SAAO;AAAA,IACL;AAAA,IACA,QAAQ;AAAA,IACR;AAAA,EACF;AACF;AAWA,eAAsB,cACpB,SACA,OACA,KACiB;AACjB,SAAO,QAAQ,EAAE,OAAO,KAAK,OAAO,CAAC,EAAE,CAAC;AAC1C;","names":[]}