@ewhauser/world-celld 0.8.1 → 0.9.1
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/CHANGELOG.md +17 -0
- package/README.md +40 -15
- package/celld-worker/README.md +9 -0
- package/celld-worker/wrangler.jsonc +3 -0
- package/dist/index.d.ts +6 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -8
- package/dist/index.js.map +1 -1
- package/dist/queue-delivery-wvPp6_Hh.d.ts.map +1 -1
- package/dist/queue-protocol-8iiZ8o_1.d.ts +42 -0
- package/dist/queue-protocol-8iiZ8o_1.d.ts.map +1 -0
- package/dist/{run-list-DGsdv6Jf.js → run-list-CHRaDRvU.js} +39 -2
- package/dist/run-list-CHRaDRvU.js.map +1 -0
- package/dist/testing.d.ts +17 -0
- package/dist/testing.d.ts.map +1 -1
- package/dist/testing.js +433 -12
- package/dist/testing.js.map +1 -1
- package/dist/worker.d.ts +14 -0
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +268 -96
- package/dist/worker.js.map +1 -1
- package/package.json +5 -4
- package/dist/run-list-DGsdv6Jf.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.9.1 (2026-10-13)
|
|
6
|
+
|
|
7
|
+
## What's Changed
|
|
8
|
+
* chore: qualify stable Workflow 5 packages for world-celld by @ewhauser in https://github.com/ewhauser/world-celld/pull/93
|
|
9
|
+
* fix: repair hook index records stranded by an interrupted creation by @ewhauser in https://github.com/ewhauser/world-celld/pull/96
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
**Full Changelog**: https://github.com/ewhauser/world-celld/compare/v0.9.0...v0.9.1
|
|
13
|
+
|
|
14
|
+
## 0.9.0 (2026-10-06)
|
|
15
|
+
|
|
16
|
+
## What's Changed
|
|
17
|
+
* feat: support World spec v8 and deliver demo queues by @ewhauser in https://github.com/ewhauser/world-celld/pull/90
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
**Full Changelog**: https://github.com/ewhauser/world-celld/compare/v0.8.1...v0.9.0
|
|
21
|
+
|
|
5
22
|
## 0.8.1 (2026-10-02)
|
|
6
23
|
|
|
7
24
|
## What's Changed
|
package/README.md
CHANGED
|
@@ -14,11 +14,18 @@ self-hosted alternative to platform-specific Workflow backends.
|
|
|
14
14
|
|
|
15
15
|
## World protocol compatibility
|
|
16
16
|
|
|
17
|
-
This adapter supports World spec
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
This adapter supports World spec v8 with the Workflow 5 stable packages. This is
|
|
18
|
+
a breaking upgrade for applications using a v7 Workflow runtime: that runtime
|
|
19
|
+
rejects a World advertising v8. This adapter also rejects event writes to runs
|
|
20
|
+
stamped v7. Keep the v7 adapter and matching Workflow packages in place until
|
|
21
|
+
those runs finish, then upgrade the application and adapter together.
|
|
22
|
+
|
|
23
|
+
Event slots are allocated in the same transaction that stores each event, so
|
|
24
|
+
reads see a dense log and failed writes leave no gaps. No background sealing or
|
|
25
|
+
`noop` events are needed.
|
|
26
|
+
|
|
27
|
+
World v8 adds forced hook claims. This adapter does not advertise the optional
|
|
28
|
+
`hookForceClaim` capability, so `experimental_force` is unavailable.
|
|
22
29
|
|
|
23
30
|
Run listing accepts either a single status or an array of statuses (OR matching).
|
|
24
31
|
An empty status array matches no runs.
|
|
@@ -85,6 +92,22 @@ const world = createCelldWorld({
|
|
|
85
92
|
`WORKFLOW_BASE_URL` (or `baseUrl`) is where the native Queue bridge delivers flow
|
|
86
93
|
requests. It must be reachable from every celld node.
|
|
87
94
|
|
|
95
|
+
For overlapping Workflow builds, give each build a distinct `CELLD_DEPLOYMENT_ID`
|
|
96
|
+
and keep its compiled workflow endpoints available while any of its runs remain
|
|
97
|
+
active. Set `WORKFLOW_DEPLOYMENT_URLS` on every app that may accept hooks for those
|
|
98
|
+
runs and in the primary celld worker's private `vars` to the same JSON map, for
|
|
99
|
+
example `{"celld-default":"https://beta.internal","stable-v1":"https://stable.internal"}`.
|
|
100
|
+
The equivalent `createCelldWorld({ deploymentUrls: { ... } })` option configures
|
|
101
|
+
an app directly. Each URL must be an absolute HTTP(S) origin or base path without
|
|
102
|
+
credentials, query, or fragment. Keep every mapped destination reachable from
|
|
103
|
+
the Queue delivery worker. A mapped run's stored deployment ID selects its
|
|
104
|
+
callback build, including for messages queued before this setting was added;
|
|
105
|
+
new messages also snapshot their destination at enqueue. Missing mappings and
|
|
106
|
+
unavailable destinations remain retryable rather than acknowledging the work.
|
|
107
|
+
Keep the map and old build in service through the last old run and pending
|
|
108
|
+
delivery. Do not change an existing deployment ID to point at different
|
|
109
|
+
compiled workflow code. The in-process test queue accepts the same map.
|
|
110
|
+
|
|
88
111
|
## Deploy the worker
|
|
89
112
|
|
|
90
113
|
Before deploying, you need a celld v0.6.0 fleet, `esbuild` on `PATH`, and an
|
|
@@ -220,16 +243,17 @@ post-compaction costs are recorded in
|
|
|
220
243
|
Application options can be passed to `createCelldWorld()` unless an environment
|
|
221
244
|
variable is shown below.
|
|
222
245
|
|
|
223
|
-
| Option | Environment variable
|
|
224
|
-
| ----------------------- |
|
|
225
|
-
| `fleetUrl` | `CELLD_FLEET_URL`
|
|
226
|
-
| `secret` | `CELLD_WORLD_SECRET`
|
|
227
|
-
| `baseUrl` | `WORKFLOW_BASE_URL`
|
|
228
|
-
| `deploymentId` | `CELLD_DEPLOYMENT_ID`
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
246
|
+
| Option | Environment variable | Default |
|
|
247
|
+
| ----------------------- | -------------------------- | ------------------------ |
|
|
248
|
+
| `fleetUrl` | `CELLD_FLEET_URL` | required |
|
|
249
|
+
| `secret` | `CELLD_WORLD_SECRET` | required with `fleetUrl` |
|
|
250
|
+
| `baseUrl` | `WORKFLOW_BASE_URL` | `http://localhost:$PORT` |
|
|
251
|
+
| `deploymentId` | `CELLD_DEPLOYMENT_ID` | `celld-default` |
|
|
252
|
+
| `deploymentUrls` | `WORKFLOW_DEPLOYMENT_URLS` | unset |
|
|
253
|
+
| `runRetentionMs` | `CELLD_RUN_RETENTION_MS` | `0` (disabled) |
|
|
254
|
+
| `streamLongPollMs` | — | `20000` |
|
|
255
|
+
| `streamFlushIntervalMs` | — | `0` |
|
|
256
|
+
| `rpcTimeoutMs` | — | `30000` (max `300000`) |
|
|
233
257
|
|
|
234
258
|
The deployed worker also accepts these celld variables:
|
|
235
259
|
|
|
@@ -237,6 +261,7 @@ The deployed worker also accepts these celld variables:
|
|
|
237
261
|
| ------------------------------- | ------- | ---------------------------------------------------- |
|
|
238
262
|
| `WORLD_SECRET` | none | Required bearer secret for RPC routes |
|
|
239
263
|
| `WORKFLOW_CALLBACK_SECRET` | none | Sent with deliveries as `x-workflow-callback-secret` |
|
|
264
|
+
| `WORKFLOW_DEPLOYMENT_URLS` | unset | JSON map of pinned deployment IDs to callback URLs |
|
|
240
265
|
| `WORKFLOW_RETENTION_MS` | `0` | Maximum run age from creation; includes active runs |
|
|
241
266
|
| `WORKFLOW_RETENTION_BATCH_SIZE` | `128` | Runs admitted by each cron sweep (maximum `1000`) |
|
|
242
267
|
|
package/celld-worker/README.md
CHANGED
|
@@ -49,6 +49,15 @@ in the primary Wrangler config and redeploy to enable a maximum workflow age
|
|
|
49
49
|
measured from run creation. celld rejects `CELLD_VAR_*` node overrides.
|
|
50
50
|
`.dev.vars` overrides apply only to `celld dev`, not production deployments.
|
|
51
51
|
|
|
52
|
+
To keep in-flight runs on their original compiled Workflow build, set
|
|
53
|
+
`vars.WORKFLOW_DEPLOYMENT_URLS` in the private primary worker config to a JSON
|
|
54
|
+
object of deployment IDs and reachable HTTP(S) callback URLs. Configure the
|
|
55
|
+
same map in the applications with `WORKFLOW_DEPLOYMENT_URLS` or `deploymentUrls`.
|
|
56
|
+
The worker resolves already queued run messages from the persisted run ID, so
|
|
57
|
+
the map must retain old destinations until their runs and queued deliveries
|
|
58
|
+
finish. Keep each old compiled app available at its mapped URL. An unknown or
|
|
59
|
+
unavailable destination is retried, including a callback 404.
|
|
60
|
+
|
|
52
61
|
`7776000000` is 90 days. The policy includes pending and running workflows as
|
|
53
62
|
well as terminal ones. Each cron occurrence admits at most
|
|
54
63
|
`WORKFLOW_RETENTION_BATCH_SIZE` runs (default `128`); the existing per-run alarm
|
|
@@ -48,6 +48,9 @@
|
|
|
48
48
|
"WORLD_SECRET": "",
|
|
49
49
|
// Optional: forwarded as x-workflow-callback-secret on queue deliveries.
|
|
50
50
|
"WORKFLOW_CALLBACK_SECRET": "",
|
|
51
|
+
// Optional trusted JSON map of persisted deployment IDs to immutable app
|
|
52
|
+
// callback URLs. Keep celld-default mapped to its original beta build.
|
|
53
|
+
"WORKFLOW_DEPLOYMENT_URLS": "",
|
|
51
54
|
// Optional fleet-wide maximum workflow age, measured from run creation.
|
|
52
55
|
// Applies to pending, running, and terminal runs. "0" disables it.
|
|
53
56
|
"WORKFLOW_RETENTION_MS": "0",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { n as NativeQueueSendOptions, r as NativeQueueSendResult, t as NativeQueueEnvelope } from "./queue-protocol-8iiZ8o_1.js";
|
|
1
2
|
import { CreateEventParams, CreateEventRequest, Event, GetChunksOptions, Hook, RunCreatedEventRequest, Step, StreamChunksResponse, StreamInfoResponse, Streamer, TerminalWorkflowRunStatus, Wait, WorkflowRun, WorkflowRunStatus, World } from "@workflow/world";
|
|
2
3
|
//#region src/retention.d.ts
|
|
3
4
|
type CleanupPhase = 'retained' | 'index' | 'streams' | 'queues' | 'payload' | 'tombstoned';
|
|
@@ -312,45 +313,9 @@ interface StreamDOId {
|
|
|
312
313
|
toString(): string;
|
|
313
314
|
}
|
|
314
315
|
//#endregion
|
|
315
|
-
//#region src/
|
|
316
|
-
/**
|
|
317
|
-
|
|
318
|
-
*
|
|
319
|
-
* Run-bearing payload bytes live in object storage so the broker's 128 kB limit does not
|
|
320
|
-
* narrow the Workflow World contract and run retention can delete those bytes
|
|
321
|
-
* without waiting for the Queue's fixed four-day retention window.
|
|
322
|
-
*/
|
|
323
|
-
interface NativeQueueEnvelope {
|
|
324
|
-
version: 1;
|
|
325
|
-
messageId: string;
|
|
326
|
-
queueName: string;
|
|
327
|
-
targetBaseUrl: string;
|
|
328
|
-
runId?: string;
|
|
329
|
-
idempotencyKey?: string;
|
|
330
|
-
payloadKey?: string;
|
|
331
|
-
/**
|
|
332
|
-
* Inline body. Health checks always travel inline; a run-bearing body may
|
|
333
|
-
* travel inline when it is small and carries no user data, in which case
|
|
334
|
-
* delivery checks the run's expiry instead of reading a payload object.
|
|
335
|
-
*/
|
|
336
|
-
body?: string;
|
|
337
|
-
/** Absolute workflow redelivery deadline; long waits are chained by the consumer. */
|
|
338
|
-
notBefore?: number;
|
|
339
|
-
/**
|
|
340
|
-
* Where the idempotency claim lives. Absent: the claim cell named by queue
|
|
341
|
-
* and key. `run`: an entry in the run's own cell (requires `runId`).
|
|
342
|
-
*/
|
|
343
|
-
claimScope?: 'run';
|
|
344
|
-
/** Failed deliveries carried across suspension re-publishes. */
|
|
345
|
-
deliveryFailures?: number;
|
|
346
|
-
suspensionCount?: number;
|
|
347
|
-
}
|
|
348
|
-
interface NativeQueueSendOptions {
|
|
349
|
-
delaySeconds?: number;
|
|
350
|
-
}
|
|
351
|
-
interface NativeQueueSendResult {
|
|
352
|
-
messageId: string;
|
|
353
|
-
}
|
|
316
|
+
//#region src/deployment-routing.d.ts
|
|
317
|
+
/** Operator-owned immutable callback destinations, keyed by stored deployment ID. */
|
|
318
|
+
type DeploymentUrls = Readonly<Record<string, string>>;
|
|
354
319
|
//#endregion
|
|
355
320
|
//#region src/queue.d.ts
|
|
356
321
|
interface CelldQueueProducer {
|
|
@@ -465,6 +430,8 @@ interface CelldWorldConfig {
|
|
|
465
430
|
* Default: process.env.WORKFLOW_BASE_URL || `http://localhost:${PORT ?? 3000}`
|
|
466
431
|
*/
|
|
467
432
|
baseUrl?: string;
|
|
433
|
+
/** Immutable callback URLs for explicitly targeted deployments. Optional. */
|
|
434
|
+
deploymentUrls?: DeploymentUrls;
|
|
468
435
|
/**
|
|
469
436
|
* Keep terminal run payloads for this many milliseconds before replacing
|
|
470
437
|
* them with metadata-only tombstones. Zero disables automatic cleanup.
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/retention.ts","../src/apply-event.ts","../src/storage.ts","../src/stream-protocol.ts","../src/streamer.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/retention.ts","../src/apply-event.ts","../src/storage.ts","../src/stream-protocol.ts","../src/streamer.ts","../src/deployment-routing.ts","../src/queue.ts","../src/run-list.ts","../src/indexes.ts","../src/config.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { $ as encodeStreamWriteBatch, A as MAX_QUEUE_DERIVED_DEADLINE_HEADROOM_MS, B as MAX_STREAM_BATCH_BYTES, C as parseDeploymentUrls, G as MAX_STREAM_READ_CHUNKS, H as MAX_STREAM_ERROR_BYTES, J as STREAM_CHUNK_LIMIT_HEADER, K as MAX_STREAM_WRITE_CHUNKS, N as boundedIntegerOption, O as MAX_FLEET_RPC_TIMEOUT_MS, P as checkedQueueTimestampAdd, R as queueDelayDeadline, S as configuredDeploymentUrl, U as MAX_STREAM_LONG_POLL_MS, V as MAX_STREAM_CHUNK_BYTES, W as MAX_STREAM_READ_BYTES, Y as decodeStreamReadResult, Z as decodeStreamWriteResult, _ as rpcStringify, b as createWorkflowIndex, c as nativeQueueDelaySeconds, g as rpcParse, h as compact, k as MAX_QUEUE_DELIVERY_TIMEOUT_MS, l as queueClaimName, n as listRunsPage, nt as normalizeStreamError, q as STREAM_BATCH_CONTENT_TYPE, tt as isChunkLimitRejection, v as parse, w as validateDeploymentUrls, x as runIndexMetadata, y as stringify, z as strictIntegerSetting } from "./run-list-CHRaDRvU.js";
|
|
2
2
|
import { CreateEventSchema, EventSchema, HookSchema, MessageId, QueuePayloadSchema, SPEC_VERSION_CURRENT, StepSchema, WaitSchema, WorkflowRunSchema, parseQueueName } from "@workflow/world";
|
|
3
3
|
import { EntityConflictError, HookNotFoundError, RunExpiredError, RunNotSupportedError, TooEarlyError, WorkflowRunNotFoundError, WorkflowWorldError } from "@workflow/errors";
|
|
4
4
|
import { monotonicFactory } from "ulid";
|
|
@@ -18,6 +18,7 @@ function resolveConfig(config) {
|
|
|
18
18
|
env: config?.env,
|
|
19
19
|
deploymentId: config?.deploymentId ?? process.env.CELLD_DEPLOYMENT_ID ?? "celld-default",
|
|
20
20
|
baseUrl: config?.baseUrl,
|
|
21
|
+
deploymentUrls: config?.deploymentUrls === void 0 ? parseDeploymentUrls(process.env.WORKFLOW_DEPLOYMENT_URLS) : validateDeploymentUrls(config.deploymentUrls),
|
|
21
22
|
runRetentionMs,
|
|
22
23
|
streamLongPollMs,
|
|
23
24
|
streamFlushIntervalMs,
|
|
@@ -609,7 +610,8 @@ function createTestPump(config) {
|
|
|
609
610
|
});
|
|
610
611
|
}
|
|
611
612
|
function retry(envelope, pathname, reason) {
|
|
612
|
-
|
|
613
|
+
const mappedRun = config.deploymentUrls !== void 0 && "runId" in envelope.message;
|
|
614
|
+
if (envelope.attempt >= maxAttempts && !mappedRun) {
|
|
613
615
|
release(envelope);
|
|
614
616
|
console.error(`[world-celld test pump] dropping ${envelope.messageId} after ${envelope.attempt} attempts: ${reason}`);
|
|
615
617
|
return;
|
|
@@ -627,7 +629,7 @@ function createTestPump(config) {
|
|
|
627
629
|
delayFor(backoff).then(() => enqueue(pathname, next));
|
|
628
630
|
}
|
|
629
631
|
async function dispatch(envelope, pathname) {
|
|
630
|
-
const url = `${
|
|
632
|
+
const url = `${envelope.targetBaseUrl}/.well-known/workflow/v1/${pathname}`;
|
|
631
633
|
const response = await fetch(url, {
|
|
632
634
|
method: "POST",
|
|
633
635
|
headers: {
|
|
@@ -639,7 +641,7 @@ function createTestPump(config) {
|
|
|
639
641
|
body: stringify(envelope.message),
|
|
640
642
|
signal: AbortSignal.timeout(httpTimeoutMs)
|
|
641
643
|
});
|
|
642
|
-
if (response.ok || PERMANENT_ERROR_STATUSES.has(response.status)) {
|
|
644
|
+
if (response.ok || !config.deploymentUrls && PERMANENT_ERROR_STATUSES.has(response.status) || !("runId" in envelope.message) && PERMANENT_ERROR_STATUSES.has(response.status)) {
|
|
643
645
|
try {
|
|
644
646
|
await response.body?.cancel();
|
|
645
647
|
} catch {}
|
|
@@ -701,6 +703,7 @@ function createTestPump(config) {
|
|
|
701
703
|
}
|
|
702
704
|
function createQueue(config) {
|
|
703
705
|
const { env, deploymentId } = config;
|
|
706
|
+
const deploymentUrls = config.deploymentUrls === void 0 ? void 0 : validateDeploymentUrls(config.deploymentUrls);
|
|
704
707
|
if (!env?.WORKFLOW_QUEUE) throw new Error("world-celld queue missing WORKFLOW_QUEUE binding");
|
|
705
708
|
const generateMessageId = monotonicFactory();
|
|
706
709
|
const testPump = createTestPump(config);
|
|
@@ -711,6 +714,17 @@ function createQueue(config) {
|
|
|
711
714
|
if (!parsedMessage.success) throw new WorkflowWorldError("world-celld queue payload is invalid", { status: 422 });
|
|
712
715
|
if (queueDelayDeadline(Date.now(), opts?.delaySeconds ?? 0, 0, 900001) === null) throw new Error("world-celld queue delaySeconds lacks required delivery headroom");
|
|
713
716
|
const runId = "runId" in parsedMessage.data && typeof parsedMessage.data.runId === "string" ? parsedMessage.data.runId : void 0;
|
|
717
|
+
const targetIds = [
|
|
718
|
+
opts?.deploymentId,
|
|
719
|
+
"runInput" in parsedMessage.data ? parsedMessage.data.runInput?.deploymentId : void 0,
|
|
720
|
+
"runContext" in parsedMessage.data ? parsedMessage.data.runContext?.deploymentId : void 0,
|
|
721
|
+
"hookInput" in parsedMessage.data ? parsedMessage.data.hookInput?.deploymentId : void 0
|
|
722
|
+
].filter((id) => typeof id === "string");
|
|
723
|
+
if (targetIds.some((id) => id !== targetIds[0])) throw new Error("world-celld: queue deployment hints disagree");
|
|
724
|
+
const targetId = targetIds[0] ?? deploymentId;
|
|
725
|
+
if (deploymentUrls && runId && targetIds.length === 0) throw new Error("world-celld: queued run has no deployment routing hint");
|
|
726
|
+
if (!deploymentUrls && opts?.deploymentId && targetId !== deploymentId) throw new Error(`world-celld: no callback URL configured for deployment ${targetId}`);
|
|
727
|
+
const targetBaseUrl = deploymentUrls ? configuredDeploymentUrl(deploymentUrls, targetId) : resolveBaseUrl(config).replace(/\/$/, "");
|
|
714
728
|
if (isTestMode()) {
|
|
715
729
|
const existing = testPump.inflight(queueName, opts?.idempotencyKey);
|
|
716
730
|
if (existing) return { messageId: existing };
|
|
@@ -720,7 +734,8 @@ function createQueue(config) {
|
|
|
720
734
|
queueName,
|
|
721
735
|
attempt: 1,
|
|
722
736
|
message: parsedMessage.data,
|
|
723
|
-
idempotencyKey: opts?.idempotencyKey
|
|
737
|
+
idempotencyKey: opts?.idempotencyKey,
|
|
738
|
+
targetBaseUrl
|
|
724
739
|
}, opts?.delaySeconds);
|
|
725
740
|
return { messageId };
|
|
726
741
|
}
|
|
@@ -731,7 +746,7 @@ function createQueue(config) {
|
|
|
731
746
|
version: 1,
|
|
732
747
|
messageId,
|
|
733
748
|
queueName,
|
|
734
|
-
targetBaseUrl
|
|
749
|
+
targetBaseUrl,
|
|
735
750
|
runId,
|
|
736
751
|
idempotencyKey: opts?.idempotencyKey,
|
|
737
752
|
body,
|
|
@@ -769,7 +784,7 @@ function createQueue(config) {
|
|
|
769
784
|
queueName: reqQueueName,
|
|
770
785
|
messageId: reqMessageId
|
|
771
786
|
});
|
|
772
|
-
if (result && typeof result.timeoutSeconds === "number") {
|
|
787
|
+
if (!("invoke" in parsedBody.data && parsedBody.data.invoke) && result !== null && typeof result === "object" && "timeoutSeconds" in result && typeof result.timeoutSeconds === "number") {
|
|
773
788
|
if (queueDelayDeadline(Date.now(), result.timeoutSeconds, 1, 900001) === null) throw new Error("Queue handler timeoutSeconds is out of range");
|
|
774
789
|
return Response.json({ timeoutSeconds: result.timeoutSeconds }, {
|
|
775
790
|
status: 503,
|
|
@@ -1488,7 +1503,8 @@ function createCelldWorld(config) {
|
|
|
1488
1503
|
const queue = createQueue({
|
|
1489
1504
|
env: { WORKFLOW_QUEUE: env.WORKFLOW_QUEUE },
|
|
1490
1505
|
deploymentId: resolved.deploymentId,
|
|
1491
|
-
baseUrl: resolved.baseUrl
|
|
1506
|
+
baseUrl: resolved.baseUrl,
|
|
1507
|
+
deploymentUrls: resolved.deploymentUrls
|
|
1492
1508
|
});
|
|
1493
1509
|
const streamer = createStreamer({
|
|
1494
1510
|
env: { WORKFLOW_STREAMS: env.WORKFLOW_STREAMS },
|