@microfox/ai-worker 1.0.2 → 1.0.3

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.js CHANGED
@@ -25,10 +25,13 @@ __export(index_exports, {
25
25
  createLambdaEntrypoint: () => createLambdaEntrypoint,
26
26
  createLambdaHandler: () => createLambdaHandler,
27
27
  createWorker: () => createWorker,
28
+ createWorkerLogger: () => createWorkerLogger,
28
29
  defineWorkerQueue: () => defineWorkerQueue,
29
30
  dispatch: () => dispatch,
30
31
  dispatchLocal: () => dispatchLocal,
31
32
  dispatchQueue: () => dispatchQueue,
33
+ dispatchWorker: () => dispatchWorker,
34
+ getQueueStartUrl: () => getQueueStartUrl,
32
35
  getWorkersConfig: () => getWorkersConfig,
33
36
  getWorkersTriggerUrl: () => getWorkersTriggerUrl,
34
37
  resolveQueueUrl: () => resolveQueueUrl,
@@ -38,10 +41,10 @@ module.exports = __toCommonJS(index_exports);
38
41
 
39
42
  // src/client.ts
40
43
  function getWorkersTriggerUrl() {
41
- 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;
44
+ const raw = process.env.WORKER_BASE_URL || process.env.WORKERS_TRIGGER_API_URL || process.env.WORKERS_CONFIG_API_URL;
42
45
  if (!raw) {
43
46
  throw new Error(
44
- "WORKER_BASE_URL (preferred) or NEXT_PUBLIC_WORKER_BASE_URL is required for background workers"
47
+ "WORKER_BASE_URL is required for background workers. Set it server-side only."
45
48
  );
46
49
  }
47
50
  const url = new URL(raw);
@@ -53,6 +56,23 @@ function getWorkersTriggerUrl() {
53
56
  url.pathname = `${basePath}/workers/trigger`.replace(/\/+$/, "");
54
57
  return url.toString();
55
58
  }
59
+ function getQueueStartUrl(queueId) {
60
+ const raw = process.env.WORKER_BASE_URL || process.env.WORKERS_TRIGGER_API_URL || process.env.WORKERS_CONFIG_API_URL;
61
+ if (!raw) {
62
+ throw new Error(
63
+ "WORKER_BASE_URL is required for background workers. Set it server-side only."
64
+ );
65
+ }
66
+ const url = new URL(raw);
67
+ url.search = "";
68
+ url.hash = "";
69
+ const path = url.pathname || "";
70
+ url.pathname = path.replace(/\/?workers\/(trigger|config)\/?$/, "");
71
+ const basePath = url.pathname.replace(/\/+$/, "");
72
+ const safeSegment = encodeURIComponent(queueId);
73
+ url.pathname = `${basePath}/queues/${safeSegment}/start`.replace(/\/+$/, "");
74
+ return url.toString();
75
+ }
56
76
  function serializeContext(ctx) {
57
77
  const serialized = {};
58
78
  if (ctx.requestId) {
@@ -110,109 +130,67 @@ async function dispatch(workerId, input, inputSchema, options, ctx) {
110
130
  jobId
111
131
  };
112
132
  }
113
- async function dispatchLocal(handler, input, ctx) {
114
- return handler({ input, ctx: ctx || {} });
115
- }
116
- async function dispatchQueue(queueId, initialInput, options = {}, ctx) {
117
- const registry = options.registry;
118
- if (!registry?.getQueueById) {
119
- throw new Error(
120
- "dispatchQueue requires options.registry with getQueueById. Use getQueueRegistry() from your workflows registry (e.g. app/api/workflows/registry/workers) and pass { registry: await getQueueRegistry() }."
121
- );
122
- }
123
- const { getQueueById, invokeMapInput } = registry;
124
- const queue = getQueueById(queueId);
125
- if (!queue) {
126
- throw new Error(`Worker queue "${queueId}" not found in registry`);
127
- }
128
- if (!queue.steps || queue.steps.length === 0) {
129
- throw new Error(`Worker queue "${queueId}" has no steps defined`);
130
- }
131
- const stepIndex = 0;
132
- const firstStep = queue.steps[stepIndex];
133
- const firstWorkerId = firstStep.workerId;
134
- if (!firstWorkerId) {
135
- throw new Error(
136
- `Worker queue "${queueId}" has an invalid first step (missing workerId)`
137
- );
138
- }
139
- let firstInput = initialInput;
140
- if (firstStep.mapInputFromPrev && typeof invokeMapInput === "function") {
141
- firstInput = await invokeMapInput(
142
- queueId,
143
- stepIndex,
144
- void 0,
145
- initialInput
146
- );
147
- }
133
+ async function dispatchWorker(workerId, input, options = {}, ctx) {
148
134
  const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
149
- const queueContext = {
150
- id: queueId,
151
- stepIndex,
152
- initialInput,
153
- queueJobId: jobId
154
- };
155
- if (options.onCreateQueueJob) {
156
- try {
157
- await options.onCreateQueueJob({
158
- queueJobId: jobId,
159
- queueId,
160
- firstStep: { workerId: firstWorkerId, workerJobId: jobId },
161
- metadata: options.metadata
162
- });
163
- } catch (err) {
164
- console.warn("[dispatchQueue] onCreateQueueJob failed:", err?.message ?? err);
165
- }
166
- }
167
- const normalizedFirstInput = firstInput !== null && typeof firstInput === "object" ? firstInput : { value: firstInput };
168
- const inputWithQueue = {
169
- ...normalizedFirstInput,
170
- __workerQueue: queueContext
171
- };
172
- const metadataWithQueue = {
173
- ...options.metadata || {},
174
- __workerQueue: queueContext
175
- };
176
135
  const triggerUrl = getWorkersTriggerUrl();
177
136
  const serializedContext = ctx ? serializeContext(ctx) : {};
178
137
  const messageBody = {
179
- workerId: firstWorkerId,
138
+ workerId,
180
139
  jobId,
181
- input: inputWithQueue,
140
+ input: input ?? {},
182
141
  context: serializedContext,
183
142
  webhookUrl: options.webhookUrl,
184
- metadata: metadataWithQueue,
143
+ metadata: options.metadata || {},
185
144
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
186
145
  };
187
- const headers = {
188
- "Content-Type": "application/json"
189
- };
146
+ const headers = { "Content-Type": "application/json" };
190
147
  const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
191
- if (triggerKey) {
192
- headers["x-workers-trigger-key"] = triggerKey;
193
- }
148
+ if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
194
149
  const response = await fetch(triggerUrl, {
150
+ method: "POST",
151
+ headers,
152
+ body: JSON.stringify({ workerId, body: messageBody })
153
+ });
154
+ if (!response.ok) {
155
+ const text = await response.text().catch(() => "");
156
+ throw new Error(
157
+ `Failed to trigger worker "${workerId}": ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
158
+ );
159
+ }
160
+ const data = await response.json().catch(() => ({}));
161
+ const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;
162
+ return { messageId, status: "queued", jobId };
163
+ }
164
+ async function dispatchLocal(handler, input, ctx) {
165
+ return handler({ input, ctx: ctx || {} });
166
+ }
167
+ async function dispatchQueue(queueId, initialInput, options = {}, _ctx) {
168
+ const jobId = options.jobId || `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
169
+ const queueStartUrl = getQueueStartUrl(queueId);
170
+ const normalizedInput = initialInput !== null && typeof initialInput === "object" ? initialInput : { value: initialInput };
171
+ const headers = { "Content-Type": "application/json" };
172
+ const triggerKey = process.env.WORKERS_TRIGGER_API_KEY;
173
+ if (triggerKey) headers["x-workers-trigger-key"] = triggerKey;
174
+ const response = await fetch(queueStartUrl, {
195
175
  method: "POST",
196
176
  headers,
197
177
  body: JSON.stringify({
198
- workerId: firstWorkerId,
199
- body: messageBody
178
+ input: normalizedInput,
179
+ initialInput: normalizedInput,
180
+ metadata: options.metadata ?? {},
181
+ jobId,
182
+ ...options.webhookUrl ? { webhookUrl: options.webhookUrl } : {}
200
183
  })
201
184
  });
202
185
  if (!response.ok) {
203
186
  const text = await response.text().catch(() => "");
204
187
  throw new Error(
205
- `Failed to trigger queue "${queueId}" (worker "${firstWorkerId}"): ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
188
+ `Failed to start queue "${queueId}": ${response.status} ${response.statusText}${text ? ` - ${text}` : ""}`
206
189
  );
207
190
  }
208
191
  const data = await response.json().catch(() => ({}));
209
- const messageId = data?.messageId ? String(data.messageId) : `trigger-${jobId}`;
210
- return {
211
- queueId,
212
- messageId,
213
- status: "queued",
214
- jobId
215
- };
192
+ const messageId = data?.messageId ?? data?.jobId ?? `queue-${jobId}`;
193
+ return { queueId, messageId, status: "queued", jobId };
216
194
  }
217
195
 
218
196
  // src/handler.ts
@@ -857,6 +835,25 @@ async function appendQueueJobStepInStore(options) {
857
835
 
858
836
  // src/handler.ts
859
837
  var SQS_MAX_DELAY_SECONDS = 900;
838
+ function createWorkerLogger(jobId, workerId) {
839
+ const prefix = (level) => `[${level}] [${workerId}] [${jobId}]`;
840
+ return {
841
+ info(msg, data) {
842
+ console.log(prefix("INFO"), msg, data !== void 0 ? JSON.stringify(data) : "");
843
+ },
844
+ warn(msg, data) {
845
+ console.warn(prefix("WARN"), msg, data !== void 0 ? JSON.stringify(data) : "");
846
+ },
847
+ error(msg, data) {
848
+ console.error(prefix("ERROR"), msg, data !== void 0 ? JSON.stringify(data) : "");
849
+ },
850
+ debug(msg, data) {
851
+ if (process.env.DEBUG || process.env.WORKER_DEBUG) {
852
+ console.debug(prefix("DEBUG"), msg, data !== void 0 ? JSON.stringify(data) : "");
853
+ }
854
+ }
855
+ };
856
+ }
860
857
  var WORKER_QUEUE_KEY = "__workerQueue";
861
858
  async function notifyQueueJobStep(queueJobId, action, params) {
862
859
  try {
@@ -889,14 +886,13 @@ async function notifyQueueJobStep(queueJobId, action, params) {
889
886
  output: params.output,
890
887
  error: params.error
891
888
  });
892
- if (process.env.DEBUG_WORKER_QUEUES === "1") {
893
- console.log("[Worker] Queue job step updated", {
894
- queueJobId,
895
- action,
896
- stepIndex: params.stepIndex,
897
- status
898
- });
899
- }
889
+ console.log("[Worker] Queue job step updated", {
890
+ queueId: params.queueId ?? queueJobId,
891
+ queueJobId,
892
+ stepIndex: params.stepIndex,
893
+ workerId: params.workerId,
894
+ status
895
+ });
900
896
  } catch (err) {
901
897
  console.warn("[Worker] Queue job update error:", {
902
898
  queueJobId,
@@ -913,20 +909,52 @@ function wrapHandlerForQueue(handler, queueRuntime) {
913
909
  return output;
914
910
  }
915
911
  const { id: queueId, stepIndex, initialInput, queueJobId } = queueContext;
912
+ const jobId = params.ctx?.jobId;
913
+ const workerId = params.ctx?.workerId ?? "";
916
914
  const next = queueRuntime.getNextStep(queueId, stepIndex);
917
- if (!next) {
918
- return output;
919
- }
920
- const childJobId = `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
921
- if (queueJobId) {
915
+ const childJobId = next ? `job-${Date.now()}-${Math.random().toString(36).slice(2, 11)}` : void 0;
916
+ if (next && queueJobId) {
922
917
  await notifyQueueJobStep(queueJobId, "append", {
923
918
  workerJobId: childJobId,
924
919
  workerId: next.workerId
925
920
  });
926
921
  }
922
+ if (queueJobId && typeof stepIndex === "number") {
923
+ await notifyQueueJobStep(queueJobId, "complete", {
924
+ queueId,
925
+ stepIndex,
926
+ workerJobId: jobId,
927
+ workerId,
928
+ output
929
+ });
930
+ }
931
+ if (!next) {
932
+ return output;
933
+ }
927
934
  let nextInput = output;
928
935
  if (next.mapInputFromPrev && typeof queueRuntime.invokeMapInput === "function") {
929
- nextInput = await queueRuntime.invokeMapInput(queueId, stepIndex + 1, output, initialInput);
936
+ let previousOutputs = [];
937
+ if (queueJobId && typeof queueRuntime.getQueueJob === "function") {
938
+ try {
939
+ const job = await queueRuntime.getQueueJob(queueJobId);
940
+ if (job?.steps) {
941
+ const fromStore = job.steps.slice(0, stepIndex).map((s, i) => ({ stepIndex: i, workerId: s.workerId, output: s.output }));
942
+ previousOutputs = fromStore.concat([
943
+ { stepIndex, workerId: params.ctx?.workerId ?? "", output }
944
+ ]);
945
+ }
946
+ } catch (e) {
947
+ if (process.env.AI_WORKER_QUEUES_DEBUG === "1") {
948
+ console.warn("[Worker] getQueueJob failed, mapping without previousOutputs:", e?.message ?? e);
949
+ }
950
+ }
951
+ }
952
+ nextInput = await queueRuntime.invokeMapInput(
953
+ queueId,
954
+ stepIndex + 1,
955
+ initialInput,
956
+ previousOutputs
957
+ );
930
958
  }
931
959
  const nextInputWithQueue = {
932
960
  ...nextInput !== null && typeof nextInput === "object" ? nextInput : { value: nextInput },
@@ -1105,6 +1133,7 @@ function createLambdaHandler(handler, outputSchema) {
1105
1133
  const handlerContext = {
1106
1134
  ...baseContext,
1107
1135
  ...jobStore ? { jobStore } : {},
1136
+ logger: createWorkerLogger(jobId, workerId),
1108
1137
  dispatchWorker: createDispatchWorker(
1109
1138
  jobId,
1110
1139
  workerId,
@@ -1115,9 +1144,12 @@ function createLambdaHandler(handler, outputSchema) {
1115
1144
  if (jobStore) {
1116
1145
  try {
1117
1146
  await jobStore.update({ status: "running" });
1147
+ const queueCtxForLog = input?.__workerQueue ?? metadata?.__workerQueue;
1118
1148
  console.log("[Worker] Job status updated to running:", {
1119
1149
  jobId,
1120
- workerId
1150
+ workerId,
1151
+ ...queueCtxForLog?.id && { queueId: queueCtxForLog.id },
1152
+ ...queueCtxForLog?.queueJobId && { queueJobId: queueCtxForLog.queueJobId }
1121
1153
  });
1122
1154
  } catch (error) {
1123
1155
  console.warn("[Worker] Failed to update status to running:", {
@@ -1147,6 +1179,7 @@ function createLambdaHandler(handler, outputSchema) {
1147
1179
  }
1148
1180
  }
1149
1181
  await notifyQueueJobStep(queueCtx.queueJobId, "start", {
1182
+ queueId: queueCtx.id,
1150
1183
  stepIndex: queueCtx.stepIndex,
1151
1184
  workerJobId: jobId,
1152
1185
  workerId,
@@ -1195,6 +1228,7 @@ function createLambdaHandler(handler, outputSchema) {
1195
1228
  const queueCtxFail = input?.__workerQueue ?? metadata?.__workerQueue;
1196
1229
  if (queueCtxFail?.queueJobId && typeof queueCtxFail.stepIndex === "number") {
1197
1230
  await notifyQueueJobStep(queueCtxFail.queueJobId, "fail", {
1231
+ queueId: queueCtxFail.id,
1198
1232
  stepIndex: queueCtxFail.stepIndex,
1199
1233
  workerJobId: jobId,
1200
1234
  workerId,
@@ -1224,15 +1258,6 @@ function createLambdaHandler(handler, outputSchema) {
1224
1258
  });
1225
1259
  }
1226
1260
  }
1227
- const queueCtxSuccess = input?.__workerQueue ?? metadata?.__workerQueue;
1228
- if (queueCtxSuccess?.queueJobId && typeof queueCtxSuccess.stepIndex === "number") {
1229
- await notifyQueueJobStep(queueCtxSuccess.queueJobId, "complete", {
1230
- stepIndex: queueCtxSuccess.stepIndex,
1231
- workerJobId: jobId,
1232
- workerId,
1233
- output
1234
- });
1235
- }
1236
1261
  console.log("[Worker] Job completed:", {
1237
1262
  jobId,
1238
1263
  workerId,
@@ -1654,6 +1679,7 @@ function createWorker(config) {
1654
1679
  const handlerContext = {
1655
1680
  ...baseContext,
1656
1681
  ...jobStore ? { jobStore } : {},
1682
+ logger: createWorkerLogger(localJobId, id),
1657
1683
  dispatchWorker: createLocalDispatchWorker(
1658
1684
  localJobId,
1659
1685
  id,
@@ -1741,10 +1767,13 @@ function createLambdaEntrypoint(agent) {
1741
1767
  createLambdaEntrypoint,
1742
1768
  createLambdaHandler,
1743
1769
  createWorker,
1770
+ createWorkerLogger,
1744
1771
  defineWorkerQueue,
1745
1772
  dispatch,
1746
1773
  dispatchLocal,
1747
1774
  dispatchQueue,
1775
+ dispatchWorker,
1776
+ getQueueStartUrl,
1748
1777
  getWorkersConfig,
1749
1778
  getWorkersTriggerUrl,
1750
1779
  resolveQueueUrl,