@workflow/web-shared 4.1.0-beta.47 → 4.1.0-beta.49
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/README.md +26 -52
- package/dist/components/error-boundary.d.ts.map +1 -0
- package/dist/{error-boundary.js → components/error-boundary.js} +1 -1
- package/dist/components/error-boundary.js.map +1 -0
- package/dist/{event-list-view.d.ts → components/event-list-view.d.ts} +2 -3
- package/dist/components/event-list-view.d.ts.map +1 -0
- package/dist/{event-list-view.js → components/event-list-view.js} +9 -17
- package/dist/components/event-list-view.js.map +1 -0
- package/dist/{hook-actions.d.ts → components/hook-actions.d.ts} +2 -3
- package/dist/components/hook-actions.d.ts.map +1 -0
- package/dist/{hook-actions.js → components/hook-actions.js} +3 -4
- package/dist/components/hook-actions.js.map +1 -0
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/index.js +8 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/run-trace-view.d.ts +22 -0
- package/dist/components/run-trace-view.d.ts.map +1 -0
- package/dist/components/run-trace-view.js +11 -0
- package/dist/components/run-trace-view.js.map +1 -0
- package/dist/components/sidebar/attribute-panel.d.ts.map +1 -0
- package/dist/{sidebar → components/sidebar}/attribute-panel.js +3 -3
- package/dist/components/sidebar/attribute-panel.js.map +1 -0
- package/dist/components/sidebar/conversation-view.d.ts.map +1 -0
- package/dist/components/sidebar/conversation-view.js.map +1 -0
- package/dist/components/sidebar/detail-card.d.ts.map +1 -0
- package/dist/components/sidebar/detail-card.js.map +1 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts +32 -0
- package/dist/components/sidebar/entity-detail-panel.d.ts.map +1 -0
- package/dist/{sidebar → components/sidebar}/entity-detail-panel.js +61 -49
- package/dist/components/sidebar/entity-detail-panel.js.map +1 -0
- package/dist/components/sidebar/events-list.d.ts +8 -0
- package/dist/components/sidebar/events-list.d.ts.map +1 -0
- package/dist/components/sidebar/events-list.js +16 -0
- package/dist/components/sidebar/events-list.js.map +1 -0
- package/dist/components/sidebar/resolve-hook-modal.d.ts.map +1 -0
- package/dist/components/sidebar/resolve-hook-modal.js.map +1 -0
- package/dist/components/stream-viewer.d.ts +18 -0
- package/dist/components/stream-viewer.d.ts.map +1 -0
- package/dist/{stream-viewer.js → components/stream-viewer.js} +1 -59
- package/dist/components/stream-viewer.js.map +1 -0
- package/dist/components/trace-viewer/components/map.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/map.js.map +1 -0
- package/dist/components/trace-viewer/components/markers.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/markers.js.map +1 -0
- package/dist/components/trace-viewer/components/node.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/node.js.map +1 -0
- package/dist/components/trace-viewer/components/search-input.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/search-input.js.map +1 -0
- package/dist/components/trace-viewer/components/search.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/search.js.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/span-detail-panel.js.map +1 -0
- package/dist/components/trace-viewer/components/ui.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/ui.js.map +1 -0
- package/dist/components/trace-viewer/components/zoom-button.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/zoom-button.js.map +1 -0
- package/dist/components/trace-viewer/components/zoom-icons.d.ts.map +1 -0
- package/dist/components/trace-viewer/components/zoom-icons.js.map +1 -0
- package/dist/components/trace-viewer/context.d.ts.map +1 -0
- package/dist/components/trace-viewer/context.js.map +1 -0
- package/dist/components/trace-viewer/index.d.ts.map +1 -0
- package/dist/components/trace-viewer/index.js.map +1 -0
- package/dist/components/trace-viewer/trace-viewer.d.ts.map +1 -0
- package/dist/components/trace-viewer/trace-viewer.js.map +1 -0
- package/dist/components/trace-viewer/types.d.ts.map +1 -0
- package/dist/components/trace-viewer/types.js.map +1 -0
- package/dist/components/trace-viewer/util/constants.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/constants.js.map +1 -0
- package/dist/components/trace-viewer/util/scrollbar-width.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/scrollbar-width.js.map +1 -0
- package/dist/{trace-viewer → components/trace-viewer}/util/timing.d.ts +1 -1
- package/dist/components/trace-viewer/util/timing.d.ts.map +1 -0
- package/dist/{trace-viewer → components/trace-viewer}/util/timing.js +1 -1
- package/dist/components/trace-viewer/util/timing.js.map +1 -0
- package/dist/components/trace-viewer/util/tree.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/tree.js.map +1 -0
- package/dist/components/trace-viewer/util/use-immediate-style.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-immediate-style.js.map +1 -0
- package/dist/components/trace-viewer/util/use-streaming-spans.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-streaming-spans.js.map +1 -0
- package/dist/components/trace-viewer/util/use-trackpad-zoom.d.ts.map +1 -0
- package/dist/components/trace-viewer/util/use-trackpad-zoom.js.map +1 -0
- package/dist/components/trace-viewer/worker.d.ts.map +1 -0
- package/dist/components/trace-viewer/worker.js.map +1 -0
- package/dist/components/workflow-trace-view.d.ts +24 -0
- package/dist/components/workflow-trace-view.d.ts.map +1 -0
- package/dist/components/workflow-trace-view.js +152 -0
- package/dist/components/workflow-trace-view.js.map +1 -0
- package/dist/components/workflow-traces/event-colors.d.ts.map +1 -0
- package/dist/components/workflow-traces/event-colors.js.map +1 -0
- package/dist/components/workflow-traces/trace-colors.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-colors.js.map +1 -0
- package/dist/components/workflow-traces/trace-span-construction.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-span-construction.js.map +1 -0
- package/dist/components/workflow-traces/trace-time-utils.d.ts.map +1 -0
- package/dist/components/workflow-traces/trace-time-utils.js.map +1 -0
- package/dist/index.d.ts +3 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -9
- package/dist/index.js.map +1 -1
- package/package.json +15 -11
- package/src/components/error-boundary.tsx +79 -0
- package/src/components/event-list-view.tsx +429 -0
- package/src/components/hook-actions.tsx +167 -0
- package/src/components/index.d.ts +1 -0
- package/src/components/index.ts +23 -0
- package/src/components/run-trace-view.tsx +75 -0
- package/src/components/sidebar/attribute-panel.tsx +938 -0
- package/src/components/sidebar/conversation-view.tsx +235 -0
- package/src/components/sidebar/detail-card.tsx +43 -0
- package/src/components/sidebar/entity-detail-panel.tsx +338 -0
- package/src/components/sidebar/events-list.tsx +119 -0
- package/src/components/sidebar/resolve-hook-modal.tsx +219 -0
- package/src/components/stream-viewer.tsx +143 -0
- package/src/components/trace-viewer/components/map.tsx +226 -0
- package/src/components/trace-viewer/components/markers.tsx +564 -0
- package/src/components/trace-viewer/components/node.tsx +259 -0
- package/src/components/trace-viewer/components/search-input.tsx +52 -0
- package/src/components/trace-viewer/components/search.tsx +47 -0
- package/src/components/trace-viewer/components/span-detail-panel.tsx +650 -0
- package/src/components/trace-viewer/components/ui.tsx +156 -0
- package/src/components/trace-viewer/components/zoom-button.tsx +61 -0
- package/src/components/trace-viewer/components/zoom-icons.tsx +65 -0
- package/src/components/trace-viewer/context.tsx +633 -0
- package/src/components/trace-viewer/index.tsx +4 -0
- package/src/components/trace-viewer/modules.d.ts +16 -0
- package/src/components/trace-viewer/trace-viewer.module.css +1292 -0
- package/src/components/trace-viewer/trace-viewer.tsx +448 -0
- package/src/components/trace-viewer/types.ts +234 -0
- package/src/components/trace-viewer/util/constants.ts +8 -0
- package/src/components/trace-viewer/util/scrollbar-width.ts +13 -0
- package/src/components/trace-viewer/util/timing.ts +33 -0
- package/src/components/trace-viewer/util/tree.ts +277 -0
- package/src/components/trace-viewer/util/use-immediate-style.ts +38 -0
- package/src/components/trace-viewer/util/use-streaming-spans.ts +415 -0
- package/src/components/trace-viewer/util/use-trackpad-zoom.tsx +51 -0
- package/src/components/trace-viewer/worker.ts +128 -0
- package/src/components/ui/alert.tsx +59 -0
- package/src/components/ui/card.tsx +88 -0
- package/src/components/ui/error-card.tsx +65 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/workflow-trace-view.tsx +306 -0
- package/src/components/workflow-traces/event-colors.ts +94 -0
- package/src/components/workflow-traces/trace-colors.ts +112 -0
- package/src/components/workflow-traces/trace-span-construction.ts +299 -0
- package/src/components/workflow-traces/trace-time-utils.ts +50 -0
- package/src/hooks/use-dark-mode.ts +34 -0
- package/src/index.d.ts +1 -0
- package/src/index.ts +29 -0
- package/src/lib/event-analysis.ts +231 -0
- package/src/lib/utils.ts +166 -0
- package/dist/api/workflow-api-client.d.ts +0 -543
- package/dist/api/workflow-api-client.d.ts.map +0 -1
- package/dist/api/workflow-api-client.js +0 -953
- package/dist/api/workflow-api-client.js.map +0 -1
- package/dist/api/workflow-server-actions.d.ts +0 -230
- package/dist/api/workflow-server-actions.d.ts.map +0 -1
- package/dist/api/workflow-server-actions.js +0 -861
- package/dist/api/workflow-server-actions.js.map +0 -1
- package/dist/error-boundary.d.ts.map +0 -1
- package/dist/error-boundary.js.map +0 -1
- package/dist/event-list-view.d.ts.map +0 -1
- package/dist/event-list-view.js.map +0 -1
- package/dist/hook-actions.d.ts.map +0 -1
- package/dist/hook-actions.js.map +0 -1
- package/dist/run-trace-view.d.ts +0 -8
- package/dist/run-trace-view.d.ts.map +0 -1
- package/dist/run-trace-view.js +0 -15
- package/dist/run-trace-view.js.map +0 -1
- package/dist/sidebar/attribute-panel.d.ts.map +0 -1
- package/dist/sidebar/attribute-panel.js.map +0 -1
- package/dist/sidebar/conversation-view.d.ts.map +0 -1
- package/dist/sidebar/conversation-view.js.map +0 -1
- package/dist/sidebar/detail-card.d.ts.map +0 -1
- package/dist/sidebar/detail-card.js.map +0 -1
- package/dist/sidebar/entity-detail-panel.d.ts +0 -12
- package/dist/sidebar/entity-detail-panel.d.ts.map +0 -1
- package/dist/sidebar/entity-detail-panel.js.map +0 -1
- package/dist/sidebar/events-list.d.ts +0 -9
- package/dist/sidebar/events-list.d.ts.map +0 -1
- package/dist/sidebar/events-list.js +0 -36
- package/dist/sidebar/events-list.js.map +0 -1
- package/dist/sidebar/resolve-hook-modal.d.ts.map +0 -1
- package/dist/sidebar/resolve-hook-modal.js.map +0 -1
- package/dist/stream-viewer.d.ts +0 -13
- package/dist/stream-viewer.d.ts.map +0 -1
- package/dist/stream-viewer.js.map +0 -1
- package/dist/trace-viewer/components/map.d.ts.map +0 -1
- package/dist/trace-viewer/components/map.js.map +0 -1
- package/dist/trace-viewer/components/markers.d.ts.map +0 -1
- package/dist/trace-viewer/components/markers.js.map +0 -1
- package/dist/trace-viewer/components/node.d.ts.map +0 -1
- package/dist/trace-viewer/components/node.js.map +0 -1
- package/dist/trace-viewer/components/search-input.d.ts.map +0 -1
- package/dist/trace-viewer/components/search-input.js.map +0 -1
- package/dist/trace-viewer/components/search.d.ts.map +0 -1
- package/dist/trace-viewer/components/search.js.map +0 -1
- package/dist/trace-viewer/components/span-detail-panel.d.ts.map +0 -1
- package/dist/trace-viewer/components/span-detail-panel.js.map +0 -1
- package/dist/trace-viewer/components/ui.d.ts.map +0 -1
- package/dist/trace-viewer/components/ui.js.map +0 -1
- package/dist/trace-viewer/components/zoom-button.d.ts.map +0 -1
- package/dist/trace-viewer/components/zoom-button.js.map +0 -1
- package/dist/trace-viewer/components/zoom-icons.d.ts.map +0 -1
- package/dist/trace-viewer/components/zoom-icons.js.map +0 -1
- package/dist/trace-viewer/context.d.ts.map +0 -1
- package/dist/trace-viewer/context.js.map +0 -1
- package/dist/trace-viewer/index.d.ts.map +0 -1
- package/dist/trace-viewer/index.js.map +0 -1
- package/dist/trace-viewer/trace-viewer.d.ts.map +0 -1
- package/dist/trace-viewer/trace-viewer.js.map +0 -1
- package/dist/trace-viewer/types.d.ts.map +0 -1
- package/dist/trace-viewer/types.js.map +0 -1
- package/dist/trace-viewer/util/constants.d.ts.map +0 -1
- package/dist/trace-viewer/util/constants.js.map +0 -1
- package/dist/trace-viewer/util/scrollbar-width.d.ts.map +0 -1
- package/dist/trace-viewer/util/scrollbar-width.js.map +0 -1
- package/dist/trace-viewer/util/timing.d.ts.map +0 -1
- package/dist/trace-viewer/util/timing.js.map +0 -1
- package/dist/trace-viewer/util/tree.d.ts.map +0 -1
- package/dist/trace-viewer/util/tree.js.map +0 -1
- package/dist/trace-viewer/util/use-immediate-style.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-immediate-style.js.map +0 -1
- package/dist/trace-viewer/util/use-streaming-spans.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-streaming-spans.js.map +0 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.d.ts.map +0 -1
- package/dist/trace-viewer/util/use-trackpad-zoom.js.map +0 -1
- package/dist/trace-viewer/worker.d.ts.map +0 -1
- package/dist/trace-viewer/worker.js.map +0 -1
- package/dist/workflow-trace-view.d.ts +0 -14
- package/dist/workflow-trace-view.d.ts.map +0 -1
- package/dist/workflow-trace-view.js +0 -135
- package/dist/workflow-trace-view.js.map +0 -1
- package/dist/workflow-traces/event-colors.d.ts.map +0 -1
- package/dist/workflow-traces/event-colors.js.map +0 -1
- package/dist/workflow-traces/trace-colors.d.ts.map +0 -1
- package/dist/workflow-traces/trace-colors.js.map +0 -1
- package/dist/workflow-traces/trace-span-construction.d.ts.map +0 -1
- package/dist/workflow-traces/trace-span-construction.js.map +0 -1
- package/dist/workflow-traces/trace-time-utils.d.ts.map +0 -1
- package/dist/workflow-traces/trace-time-utils.js.map +0 -1
- package/server/README.md +0 -1
- package/server/package.json +0 -4
- /package/dist/{error-boundary.d.ts → components/error-boundary.d.ts} +0 -0
- /package/dist/{sidebar → components/sidebar}/attribute-panel.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/conversation-view.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/conversation-view.js +0 -0
- /package/dist/{sidebar → components/sidebar}/detail-card.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/detail-card.js +0 -0
- /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.d.ts +0 -0
- /package/dist/{sidebar → components/sidebar}/resolve-hook-modal.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/map.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/map.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/markers.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/markers.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/node.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/node.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search-input.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/search.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/span-detail-panel.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/ui.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/ui.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-button.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/components/zoom-icons.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/context.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/context.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/index.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/index.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/trace-viewer.module.css +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/types.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/types.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/constants.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/constants.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/scrollbar-width.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/tree.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/tree.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-immediate-style.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-streaming-spans.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/util/use-trackpad-zoom.js +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/worker.d.ts +0 -0
- /package/dist/{trace-viewer → components/trace-viewer}/worker.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/event-colors.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/event-colors.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-colors.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-span-construction.js +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.d.ts +0 -0
- /package/dist/{workflow-traces → components/workflow-traces}/trace-time-utils.js +0 -0
|
@@ -1,861 +0,0 @@
|
|
|
1
|
-
'use server';
|
|
2
|
-
import fs from 'node:fs/promises';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { hydrateResourceIO } from '@workflow/core/observability';
|
|
5
|
-
import { createWorld, healthCheck, resumeHook as resumeHookRuntime, start, } from '@workflow/core/runtime';
|
|
6
|
-
import { getDeserializeStream, getExternalRevivers, } from '@workflow/core/serialization';
|
|
7
|
-
import { WorkflowAPIError, WorkflowRunNotFoundError } from '@workflow/errors';
|
|
8
|
-
import { findWorkflowDataDir } from '@workflow/utils/check-data-dir';
|
|
9
|
-
import { isLegacySpecVersion, SPEC_VERSION_LEGACY, } from '@workflow/world';
|
|
10
|
-
import { createVercelWorld } from '@workflow/world-vercel';
|
|
11
|
-
/**
|
|
12
|
-
* Map from WORKFLOW_TARGET_WORLD value to human-readable display name
|
|
13
|
-
*/
|
|
14
|
-
function getBackendDisplayName(targetWorld) {
|
|
15
|
-
if (!targetWorld)
|
|
16
|
-
return 'Local';
|
|
17
|
-
switch (targetWorld) {
|
|
18
|
-
case 'local':
|
|
19
|
-
return 'Local';
|
|
20
|
-
case 'vercel':
|
|
21
|
-
return 'Vercel';
|
|
22
|
-
case '@workflow/world-postgres':
|
|
23
|
-
case 'postgres':
|
|
24
|
-
return 'PostgreSQL';
|
|
25
|
-
default:
|
|
26
|
-
// For custom worlds, try to make a readable name
|
|
27
|
-
if (targetWorld.startsWith('@')) {
|
|
28
|
-
// Extract package name without scope for display
|
|
29
|
-
const parts = targetWorld.split('/');
|
|
30
|
-
return parts[parts.length - 1] || targetWorld;
|
|
31
|
-
}
|
|
32
|
-
return targetWorld;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
function getEffectiveBackendId() {
|
|
36
|
-
const targetWorld = process.env.WORKFLOW_TARGET_WORLD;
|
|
37
|
-
if (targetWorld) {
|
|
38
|
-
return targetWorld;
|
|
39
|
-
}
|
|
40
|
-
// Match @workflow/core/runtime defaulting: vercel if VERCEL_DEPLOYMENT_ID is set, else local.
|
|
41
|
-
return process.env.VERCEL_DEPLOYMENT_ID ? 'vercel' : 'local';
|
|
42
|
-
}
|
|
43
|
-
function getObservabilityCwd() {
|
|
44
|
-
const raw = process.env.WORKFLOW_OBSERVABILITY_CWD;
|
|
45
|
-
if (!raw) {
|
|
46
|
-
return process.cwd();
|
|
47
|
-
}
|
|
48
|
-
return path.isAbsolute(raw) ? raw : path.resolve(process.cwd(), raw);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Ensure local-world env is derived consistently when running `packages/web` directly.
|
|
52
|
-
*
|
|
53
|
-
* Without this, the UI may *display* a dataDir detected from WORKFLOW_OBSERVABILITY_CWD,
|
|
54
|
-
* while the actual World reads from `WORKFLOW_LOCAL_DATA_DIR` (defaulting to `.workflow-data`
|
|
55
|
-
* under the web package cwd), resulting in "no runs" even though data exists.
|
|
56
|
-
*/
|
|
57
|
-
async function ensureLocalWorldDataDirEnv() {
|
|
58
|
-
if (process.env.WORKFLOW_LOCAL_DATA_DIR)
|
|
59
|
-
return;
|
|
60
|
-
const cwd = getObservabilityCwd();
|
|
61
|
-
const info = await findWorkflowDataDir(cwd);
|
|
62
|
-
// Prefer a discovered workflow-data directory (e.g. `.next/workflow-data`).
|
|
63
|
-
if (info.dataDir) {
|
|
64
|
-
process.env.WORKFLOW_LOCAL_DATA_DIR = info.dataDir;
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// Fall back to a canonical location under the target project directory.
|
|
68
|
-
process.env.WORKFLOW_LOCAL_DATA_DIR = path.resolve(cwd, '.workflow-data');
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Extract hostname from a database URL without exposing credentials.
|
|
72
|
-
*/
|
|
73
|
-
function extractHostnameFromUrl(url) {
|
|
74
|
-
if (!url)
|
|
75
|
-
return undefined;
|
|
76
|
-
try {
|
|
77
|
-
const parsed = new URL(url);
|
|
78
|
-
return parsed.hostname || undefined;
|
|
79
|
-
}
|
|
80
|
-
catch {
|
|
81
|
-
return undefined;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Extract database name from a URL where pathname is like "/dbname".
|
|
86
|
-
* (Works for postgres/mongodb-style URLs; returns undefined when not applicable.)
|
|
87
|
-
*/
|
|
88
|
-
function extractDatabaseFromUrl(url) {
|
|
89
|
-
if (!url)
|
|
90
|
-
return undefined;
|
|
91
|
-
try {
|
|
92
|
-
const parsed = new URL(url);
|
|
93
|
-
const dbName = parsed.pathname?.slice(1);
|
|
94
|
-
return dbName || undefined;
|
|
95
|
-
}
|
|
96
|
-
catch {
|
|
97
|
-
return undefined;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// Keep this list in sync with `worlds-manifest.json` env + credentialsNote.
|
|
101
|
-
const WORLD_ENV_ALLOWLIST_BY_TARGET_WORLD = {
|
|
102
|
-
// Official
|
|
103
|
-
local: [
|
|
104
|
-
'WORKFLOW_TARGET_WORLD',
|
|
105
|
-
'WORKFLOW_LOCAL_DATA_DIR',
|
|
106
|
-
'WORKFLOW_MANIFEST_PATH',
|
|
107
|
-
'WORKFLOW_OBSERVABILITY_CWD',
|
|
108
|
-
'PORT',
|
|
109
|
-
],
|
|
110
|
-
'@workflow/world-local': [
|
|
111
|
-
'WORKFLOW_TARGET_WORLD',
|
|
112
|
-
'WORKFLOW_LOCAL_DATA_DIR',
|
|
113
|
-
'WORKFLOW_MANIFEST_PATH',
|
|
114
|
-
'WORKFLOW_OBSERVABILITY_CWD',
|
|
115
|
-
'PORT',
|
|
116
|
-
],
|
|
117
|
-
postgres: ['WORKFLOW_TARGET_WORLD', 'WORKFLOW_POSTGRES_URL'],
|
|
118
|
-
'@workflow/world-postgres': [
|
|
119
|
-
'WORKFLOW_TARGET_WORLD',
|
|
120
|
-
'WORKFLOW_POSTGRES_URL',
|
|
121
|
-
],
|
|
122
|
-
vercel: [
|
|
123
|
-
'WORKFLOW_TARGET_WORLD',
|
|
124
|
-
'WORKFLOW_VERCEL_ENV',
|
|
125
|
-
'WORKFLOW_VERCEL_TEAM',
|
|
126
|
-
'WORKFLOW_VERCEL_PROJECT',
|
|
127
|
-
'WORKFLOW_VERCEL_AUTH_TOKEN',
|
|
128
|
-
],
|
|
129
|
-
'@workflow/world-vercel': [
|
|
130
|
-
'WORKFLOW_TARGET_WORLD',
|
|
131
|
-
'WORKFLOW_VERCEL_ENV',
|
|
132
|
-
'WORKFLOW_VERCEL_TEAM',
|
|
133
|
-
'WORKFLOW_VERCEL_PROJECT',
|
|
134
|
-
'WORKFLOW_VERCEL_AUTH_TOKEN',
|
|
135
|
-
],
|
|
136
|
-
// Community (from worlds-manifest.json)
|
|
137
|
-
'@workflow-worlds/starter': ['WORKFLOW_TARGET_WORLD'],
|
|
138
|
-
'@workflow-worlds/turso': [
|
|
139
|
-
'WORKFLOW_TARGET_WORLD',
|
|
140
|
-
'WORKFLOW_TURSO_DATABASE_URL',
|
|
141
|
-
],
|
|
142
|
-
'@workflow-worlds/mongodb': [
|
|
143
|
-
'WORKFLOW_TARGET_WORLD',
|
|
144
|
-
'WORKFLOW_MONGODB_URI',
|
|
145
|
-
'WORKFLOW_MONGODB_DATABASE_NAME',
|
|
146
|
-
],
|
|
147
|
-
'@workflow-worlds/redis': ['WORKFLOW_TARGET_WORLD', 'WORKFLOW_REDIS_URI'],
|
|
148
|
-
'workflow-world-jazz': [
|
|
149
|
-
'WORKFLOW_TARGET_WORLD',
|
|
150
|
-
// credentialsNote:
|
|
151
|
-
'JAZZ_API_KEY',
|
|
152
|
-
'JAZZ_WORKER_ACCOUNT',
|
|
153
|
-
'JAZZ_WORKER_SECRET',
|
|
154
|
-
],
|
|
155
|
-
};
|
|
156
|
-
function getAllowedEnvKeysForBackend(backendId) {
|
|
157
|
-
return (WORLD_ENV_ALLOWLIST_BY_TARGET_WORLD[backendId] ?? ['WORKFLOW_TARGET_WORLD']);
|
|
158
|
-
}
|
|
159
|
-
// Keep this list in sync with `worlds-manifest.json` env + credentialsNote.
|
|
160
|
-
//
|
|
161
|
-
// IMPORTANT: This is intentionally explicit (no heuristics). We only redact values for env
|
|
162
|
-
// vars that are known + whitelisted and that we *know* contain secrets/credentials.
|
|
163
|
-
const WORLD_SENSITIVE_ENV_KEYS = new Set([
|
|
164
|
-
// Official
|
|
165
|
-
'WORKFLOW_POSTGRES_URL',
|
|
166
|
-
'WORKFLOW_VERCEL_AUTH_TOKEN',
|
|
167
|
-
// Community
|
|
168
|
-
'WORKFLOW_TURSO_DATABASE_URL',
|
|
169
|
-
'WORKFLOW_MONGODB_URI',
|
|
170
|
-
'WORKFLOW_REDIS_URI',
|
|
171
|
-
'JAZZ_API_KEY',
|
|
172
|
-
'JAZZ_WORKER_SECRET',
|
|
173
|
-
]);
|
|
174
|
-
function isSet(value) {
|
|
175
|
-
return value !== undefined && value !== null && value !== '';
|
|
176
|
-
}
|
|
177
|
-
function deriveDbInfoForKey(key, value) {
|
|
178
|
-
// Only attempt for URL-like strings.
|
|
179
|
-
if (!value.includes(':'))
|
|
180
|
-
return null;
|
|
181
|
-
try {
|
|
182
|
-
const parsed = new URL(value);
|
|
183
|
-
const protocol = (parsed.protocol || '').replace(':', '');
|
|
184
|
-
// file: URIs are not useful for hostname/db display
|
|
185
|
-
if (protocol === 'file')
|
|
186
|
-
return null;
|
|
187
|
-
const hostname = extractHostnameFromUrl(value);
|
|
188
|
-
const database = extractDatabaseFromUrl(value);
|
|
189
|
-
const out = {};
|
|
190
|
-
if (hostname)
|
|
191
|
-
out[`derived.${key}.hostname`] = hostname;
|
|
192
|
-
if (database)
|
|
193
|
-
out[`derived.${key}.database`] = database;
|
|
194
|
-
if (protocol)
|
|
195
|
-
out[`derived.${key}.protocol`] = protocol;
|
|
196
|
-
return Object.keys(out).length ? out : null;
|
|
197
|
-
}
|
|
198
|
-
catch {
|
|
199
|
-
return null;
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
async function getLocalDisplayInfo() {
|
|
203
|
-
const cwd = getObservabilityCwd();
|
|
204
|
-
const dataDirInfo = await findWorkflowDataDir(cwd);
|
|
205
|
-
const out = {
|
|
206
|
-
'local.shortName': dataDirInfo.shortName,
|
|
207
|
-
'local.projectDir': dataDirInfo.projectDir,
|
|
208
|
-
};
|
|
209
|
-
if (dataDirInfo.dataDir) {
|
|
210
|
-
out['local.dataDirPath'] = dataDirInfo.dataDir;
|
|
211
|
-
}
|
|
212
|
-
return out;
|
|
213
|
-
}
|
|
214
|
-
function collectAllowedEnv(allowedKeys) {
|
|
215
|
-
const publicEnv = {};
|
|
216
|
-
const sensitiveEnvKeys = [];
|
|
217
|
-
const derivedDisplayInfo = {};
|
|
218
|
-
for (const key of allowedKeys) {
|
|
219
|
-
const value = process.env[key];
|
|
220
|
-
if (!isSet(value))
|
|
221
|
-
continue;
|
|
222
|
-
if (WORLD_SENSITIVE_ENV_KEYS.has(key)) {
|
|
223
|
-
sensitiveEnvKeys.push(key);
|
|
224
|
-
const derived = deriveDbInfoForKey(key, value);
|
|
225
|
-
if (derived)
|
|
226
|
-
Object.assign(derivedDisplayInfo, derived);
|
|
227
|
-
continue;
|
|
228
|
-
}
|
|
229
|
-
publicEnv[key] = value;
|
|
230
|
-
}
|
|
231
|
-
return {
|
|
232
|
-
publicEnv,
|
|
233
|
-
sensitiveEnvKeys: Array.from(new Set(sensitiveEnvKeys)).sort(),
|
|
234
|
-
derivedDisplayInfo,
|
|
235
|
-
};
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* Get public configuration info that is safe to send to the client.
|
|
239
|
-
*
|
|
240
|
-
* This is the ONLY server action that intentionally exposes env-derived data,
|
|
241
|
-
* and that data is strictly whitelisted per world backend.
|
|
242
|
-
*/
|
|
243
|
-
export async function getPublicServerConfig() {
|
|
244
|
-
const backendId = getEffectiveBackendId();
|
|
245
|
-
const backendDisplayName = getBackendDisplayName(backendId);
|
|
246
|
-
const allowedKeys = getAllowedEnvKeysForBackend(backendId);
|
|
247
|
-
const { publicEnv, sensitiveEnvKeys, derivedDisplayInfo } = collectAllowedEnv(allowedKeys);
|
|
248
|
-
const displayInfo = { ...derivedDisplayInfo };
|
|
249
|
-
if (backendId === 'local' || backendId === '@workflow/world-local') {
|
|
250
|
-
Object.assign(displayInfo, await getLocalDisplayInfo());
|
|
251
|
-
}
|
|
252
|
-
const config = {
|
|
253
|
-
backendDisplayName,
|
|
254
|
-
backendId,
|
|
255
|
-
publicEnv,
|
|
256
|
-
sensitiveEnvKeys,
|
|
257
|
-
displayInfo: Object.keys(displayInfo).length ? displayInfo : undefined,
|
|
258
|
-
};
|
|
259
|
-
// Provide defaults for commonly expected keys without revealing extra secrets.
|
|
260
|
-
if ((backendId === 'vercel' || backendId === '@workflow/world-vercel') &&
|
|
261
|
-
!publicEnv.WORKFLOW_VERCEL_ENV) {
|
|
262
|
-
config.publicEnv.WORKFLOW_VERCEL_ENV = 'production';
|
|
263
|
-
}
|
|
264
|
-
return config;
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Cache for World instances.
|
|
268
|
-
*
|
|
269
|
-
* IMPORTANT:
|
|
270
|
-
* - We only cache non-vercel worlds.
|
|
271
|
-
* - Cache keys are derived from **server-side** WORKFLOW_* env vars only.
|
|
272
|
-
*/
|
|
273
|
-
const worldCache = new Map();
|
|
274
|
-
/**
|
|
275
|
-
* Get or create a World instance based on configuration.
|
|
276
|
-
*
|
|
277
|
-
* The @workflow/web UI should always pass `{}` for envMap.
|
|
278
|
-
*/
|
|
279
|
-
async function getWorldFromEnv(userEnvMap) {
|
|
280
|
-
const backendId = getEffectiveBackendId();
|
|
281
|
-
const isVercelWorld = ['vercel', '@workflow/world-vercel'].includes(backendId);
|
|
282
|
-
// For the vercel world specifically, we do not cache the world,
|
|
283
|
-
// and allow user-provided env, as it can be a multi-tenant environment,
|
|
284
|
-
// and we instantiate the world per-user directly to avoid having to set
|
|
285
|
-
// process.env.
|
|
286
|
-
if (isVercelWorld) {
|
|
287
|
-
return createVercelWorld({
|
|
288
|
-
token: userEnvMap.WORKFLOW_VERCEL_AUTH_TOKEN ||
|
|
289
|
-
process.env.WORKFLOW_VERCEL_AUTH_TOKEN,
|
|
290
|
-
projectConfig: {
|
|
291
|
-
environment: userEnvMap.WORKFLOW_VERCEL_ENV || process.env.WORKFLOW_VERCEL_ENV,
|
|
292
|
-
projectId: userEnvMap.WORKFLOW_VERCEL_PROJECT ||
|
|
293
|
-
process.env.WORKFLOW_VERCEL_PROJECT,
|
|
294
|
-
teamId: userEnvMap.WORKFLOW_VERCEL_TEAM || process.env.WORKFLOW_VERCEL_TEAM,
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
// For other worlds, we intentionally do not trust or apply client-provided env,
|
|
299
|
-
// to avoid potential security risks in self-hosted scenarios.
|
|
300
|
-
// Ensure local-world reads from the same project directory the UI is inspecting.
|
|
301
|
-
if (backendId === 'local' || backendId === '@workflow/world-local') {
|
|
302
|
-
await ensureLocalWorldDataDirEnv();
|
|
303
|
-
}
|
|
304
|
-
// Cache key derived ONLY from WORKFLOW_* env vars.
|
|
305
|
-
const workflowEnvEntries = Object.entries(process.env).filter(([key]) => key.startsWith('WORKFLOW_'));
|
|
306
|
-
workflowEnvEntries.sort(([a], [b]) => a.localeCompare(b));
|
|
307
|
-
const cacheKey = JSON.stringify(Object.fromEntries(workflowEnvEntries));
|
|
308
|
-
const cachedWorld = worldCache.get(cacheKey);
|
|
309
|
-
if (cachedWorld) {
|
|
310
|
-
return cachedWorld;
|
|
311
|
-
}
|
|
312
|
-
const world = createWorld();
|
|
313
|
-
worldCache.set(cacheKey, world);
|
|
314
|
-
return world;
|
|
315
|
-
}
|
|
316
|
-
/**
|
|
317
|
-
* Creates a structured error object from a caught error
|
|
318
|
-
*/
|
|
319
|
-
function createServerActionError(error, operation, requestParams) {
|
|
320
|
-
const err = error instanceof Error ? error : new Error(String(error));
|
|
321
|
-
console.error(`[web-api] ${operation} error:`, err);
|
|
322
|
-
let errorResponse;
|
|
323
|
-
if (WorkflowAPIError.is(error)) {
|
|
324
|
-
// If the World threw the error on fetch/fs.read, we add that data
|
|
325
|
-
// to the error object
|
|
326
|
-
errorResponse = {
|
|
327
|
-
message: getUserFacingErrorMessage(err, error.status),
|
|
328
|
-
layer: 'API',
|
|
329
|
-
cause: err.stack || err.message,
|
|
330
|
-
request: {
|
|
331
|
-
operation,
|
|
332
|
-
params: requestParams ?? {},
|
|
333
|
-
status: error.status,
|
|
334
|
-
url: error.url,
|
|
335
|
-
code: error.code ?? undefined,
|
|
336
|
-
},
|
|
337
|
-
};
|
|
338
|
-
}
|
|
339
|
-
else if (WorkflowRunNotFoundError.is(error)) {
|
|
340
|
-
// The World might repackage the error as a WorkflowRunNotFoundError
|
|
341
|
-
errorResponse = {
|
|
342
|
-
message: getUserFacingErrorMessage(error, 404),
|
|
343
|
-
layer: 'API',
|
|
344
|
-
cause: err.stack || err.message,
|
|
345
|
-
request: { operation, status: 404, params: requestParams ?? {} },
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
else {
|
|
349
|
-
errorResponse = {
|
|
350
|
-
message: getUserFacingErrorMessage(err),
|
|
351
|
-
layer: 'server',
|
|
352
|
-
cause: err.stack || err.message,
|
|
353
|
-
request: { status: 500, operation, params: requestParams ?? {} },
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
return {
|
|
357
|
-
success: false,
|
|
358
|
-
error: errorResponse,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
/**
|
|
362
|
-
* Converts an error into a user-facing message
|
|
363
|
-
*/
|
|
364
|
-
function getUserFacingErrorMessage(error, status) {
|
|
365
|
-
if (!status) {
|
|
366
|
-
return `Error creating response: ${error.message}`;
|
|
367
|
-
}
|
|
368
|
-
// Check for common error patterns
|
|
369
|
-
if (status === 403 || status === 401) {
|
|
370
|
-
return 'Access denied. Please check your credentials and permissions.';
|
|
371
|
-
}
|
|
372
|
-
if (status === 404) {
|
|
373
|
-
return 'The requested resource was not found.';
|
|
374
|
-
}
|
|
375
|
-
if (status === 500) {
|
|
376
|
-
return 'Error connecting to World backend, please try again later.';
|
|
377
|
-
}
|
|
378
|
-
if (error.message?.includes('Network') || error.message?.includes('fetch')) {
|
|
379
|
-
return 'Network error. Please check your connection and try again.';
|
|
380
|
-
}
|
|
381
|
-
// Return the original message for other errors
|
|
382
|
-
return error.message || 'An unexpected error occurred';
|
|
383
|
-
}
|
|
384
|
-
const toJSONCompatible = (data) => {
|
|
385
|
-
if (data && typeof data === 'object') {
|
|
386
|
-
return JSON.parse(JSON.stringify(data));
|
|
387
|
-
}
|
|
388
|
-
return data;
|
|
389
|
-
};
|
|
390
|
-
const hydrate = (data) => {
|
|
391
|
-
try {
|
|
392
|
-
return hydrateResourceIO(data);
|
|
393
|
-
}
|
|
394
|
-
catch (error) {
|
|
395
|
-
throw new Error('Failed to hydrate data', { cause: error });
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
/**
|
|
399
|
-
* Helper to create successful responses
|
|
400
|
-
* @param data - The data to return on success
|
|
401
|
-
* @returns ServerActionResult with success=true and the data
|
|
402
|
-
*/
|
|
403
|
-
function createResponse(data) {
|
|
404
|
-
data = toJSONCompatible(data);
|
|
405
|
-
return {
|
|
406
|
-
success: true,
|
|
407
|
-
data,
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
/**
|
|
411
|
-
* Fetch paginated list of workflow runs
|
|
412
|
-
*/
|
|
413
|
-
export async function fetchRuns(worldEnv, params) {
|
|
414
|
-
const { cursor, sortOrder = 'desc', limit = 10, workflowName, status, } = params;
|
|
415
|
-
try {
|
|
416
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
417
|
-
const result = await world.runs.list({
|
|
418
|
-
...(workflowName ? { workflowName } : {}),
|
|
419
|
-
...(status ? { status: status } : {}),
|
|
420
|
-
pagination: { cursor, limit, sortOrder },
|
|
421
|
-
resolveData: 'none',
|
|
422
|
-
});
|
|
423
|
-
return createResponse({
|
|
424
|
-
data: result.data.map(hydrate),
|
|
425
|
-
cursor: result.cursor ?? undefined,
|
|
426
|
-
hasMore: result.hasMore,
|
|
427
|
-
});
|
|
428
|
-
}
|
|
429
|
-
catch (error) {
|
|
430
|
-
return createServerActionError(error, 'world.runs.list', params);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
/**
|
|
434
|
-
* Fetch a single workflow run with full data
|
|
435
|
-
*/
|
|
436
|
-
export async function fetchRun(worldEnv, runId, resolveData = 'all') {
|
|
437
|
-
try {
|
|
438
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
439
|
-
const run = await world.runs.get(runId, { resolveData });
|
|
440
|
-
const hydratedRun = hydrate(run);
|
|
441
|
-
return createResponse(hydratedRun);
|
|
442
|
-
}
|
|
443
|
-
catch (error) {
|
|
444
|
-
return createServerActionError(error, 'world.runs.get', {
|
|
445
|
-
runId,
|
|
446
|
-
resolveData,
|
|
447
|
-
});
|
|
448
|
-
}
|
|
449
|
-
}
|
|
450
|
-
/**
|
|
451
|
-
* Fetch paginated list of steps for a run
|
|
452
|
-
*/
|
|
453
|
-
export async function fetchSteps(worldEnv, runId, params) {
|
|
454
|
-
const { cursor, sortOrder = 'asc', limit = 100 } = params;
|
|
455
|
-
try {
|
|
456
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
457
|
-
const result = await world.steps.list({
|
|
458
|
-
runId,
|
|
459
|
-
pagination: { cursor, limit, sortOrder },
|
|
460
|
-
resolveData: 'none',
|
|
461
|
-
});
|
|
462
|
-
return createResponse({
|
|
463
|
-
// StepWithoutData has undefined input/output, but after hydration the structure is compatible
|
|
464
|
-
data: result.data.map(hydrate),
|
|
465
|
-
cursor: result.cursor ?? undefined,
|
|
466
|
-
hasMore: result.hasMore,
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
catch (error) {
|
|
470
|
-
return createServerActionError(error, 'world.steps.list', {
|
|
471
|
-
runId,
|
|
472
|
-
...params,
|
|
473
|
-
});
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
/**
|
|
477
|
-
* Fetch a single step with full data
|
|
478
|
-
*/
|
|
479
|
-
export async function fetchStep(worldEnv, runId, stepId, resolveData = 'all') {
|
|
480
|
-
try {
|
|
481
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
482
|
-
const step = await world.steps.get(runId, stepId, { resolveData });
|
|
483
|
-
const hydratedStep = hydrate(step);
|
|
484
|
-
return createResponse(hydratedStep);
|
|
485
|
-
}
|
|
486
|
-
catch (error) {
|
|
487
|
-
return createServerActionError(error, 'world.steps.get', {
|
|
488
|
-
runId,
|
|
489
|
-
stepId,
|
|
490
|
-
resolveData,
|
|
491
|
-
});
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
/**
|
|
495
|
-
* Fetch paginated list of events for a run
|
|
496
|
-
*/
|
|
497
|
-
export async function fetchEvents(worldEnv, runId, params) {
|
|
498
|
-
const { cursor, sortOrder = 'asc', limit = 1000 } = params;
|
|
499
|
-
try {
|
|
500
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
501
|
-
const result = await world.events.list({
|
|
502
|
-
runId,
|
|
503
|
-
pagination: { cursor, limit, sortOrder },
|
|
504
|
-
resolveData: 'none',
|
|
505
|
-
});
|
|
506
|
-
return createResponse({
|
|
507
|
-
data: result.data,
|
|
508
|
-
cursor: result.cursor ?? undefined,
|
|
509
|
-
hasMore: result.hasMore,
|
|
510
|
-
});
|
|
511
|
-
}
|
|
512
|
-
catch (error) {
|
|
513
|
-
return createServerActionError(error, 'world.events.list', {
|
|
514
|
-
runId,
|
|
515
|
-
...params,
|
|
516
|
-
});
|
|
517
|
-
}
|
|
518
|
-
}
|
|
519
|
-
/**
|
|
520
|
-
* Fetch events by correlation ID
|
|
521
|
-
*/
|
|
522
|
-
export async function fetchEventsByCorrelationId(worldEnv, correlationId, params) {
|
|
523
|
-
const { cursor, sortOrder = 'asc', limit = 1000, withData = false } = params;
|
|
524
|
-
try {
|
|
525
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
526
|
-
const result = await world.events.listByCorrelationId({
|
|
527
|
-
correlationId,
|
|
528
|
-
pagination: { cursor, limit, sortOrder },
|
|
529
|
-
resolveData: withData ? 'all' : 'none',
|
|
530
|
-
});
|
|
531
|
-
return createResponse({
|
|
532
|
-
data: result.data.map(hydrate),
|
|
533
|
-
cursor: result.cursor ?? undefined,
|
|
534
|
-
hasMore: result.hasMore,
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
catch (error) {
|
|
538
|
-
return createServerActionError(error, 'world.events.listByCorrelationId', {
|
|
539
|
-
correlationId,
|
|
540
|
-
...params,
|
|
541
|
-
});
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
/**
|
|
545
|
-
* Fetch paginated list of hooks
|
|
546
|
-
*/
|
|
547
|
-
export async function fetchHooks(worldEnv, params) {
|
|
548
|
-
const { runId, cursor, sortOrder = 'desc', limit = 10 } = params;
|
|
549
|
-
try {
|
|
550
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
551
|
-
const result = await world.hooks.list({
|
|
552
|
-
...(runId ? { runId } : {}),
|
|
553
|
-
pagination: { cursor, limit, sortOrder },
|
|
554
|
-
resolveData: 'none',
|
|
555
|
-
});
|
|
556
|
-
return createResponse({
|
|
557
|
-
data: result.data.map(hydrate),
|
|
558
|
-
cursor: result.cursor ?? undefined,
|
|
559
|
-
hasMore: result.hasMore,
|
|
560
|
-
});
|
|
561
|
-
}
|
|
562
|
-
catch (error) {
|
|
563
|
-
return createServerActionError(error, 'world.hooks.list', params);
|
|
564
|
-
}
|
|
565
|
-
}
|
|
566
|
-
/**
|
|
567
|
-
* Fetch a single hook with full data
|
|
568
|
-
*/
|
|
569
|
-
export async function fetchHook(worldEnv, hookId, resolveData = 'all') {
|
|
570
|
-
try {
|
|
571
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
572
|
-
const hook = await world.hooks.get(hookId, { resolveData });
|
|
573
|
-
return createResponse(hydrate(hook));
|
|
574
|
-
}
|
|
575
|
-
catch (error) {
|
|
576
|
-
return createServerActionError(error, 'world.hooks.get', {
|
|
577
|
-
hookId,
|
|
578
|
-
resolveData,
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
/**
|
|
583
|
-
* Cancel a workflow run
|
|
584
|
-
*/
|
|
585
|
-
export async function cancelRun(worldEnv, runId) {
|
|
586
|
-
try {
|
|
587
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
588
|
-
const run = await world.runs.get(runId, { resolveData: 'none' });
|
|
589
|
-
const compatMode = isLegacySpecVersion(run.specVersion);
|
|
590
|
-
const eventData = {
|
|
591
|
-
eventType: 'run_cancelled',
|
|
592
|
-
specVersion: run.specVersion || 1,
|
|
593
|
-
};
|
|
594
|
-
await world.events.create(runId, eventData, { v1Compat: compatMode });
|
|
595
|
-
return createResponse(undefined);
|
|
596
|
-
}
|
|
597
|
-
catch (error) {
|
|
598
|
-
return createServerActionError(error, 'world.events.create', {
|
|
599
|
-
runId,
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
}
|
|
603
|
-
/**
|
|
604
|
-
* Start a new workflow run.
|
|
605
|
-
*
|
|
606
|
-
* This requires the ID of an existing run of which to re-use the deployment ID of.
|
|
607
|
-
*/
|
|
608
|
-
export async function recreateRun(worldEnv, runId, deploymentId) {
|
|
609
|
-
try {
|
|
610
|
-
const world = await getWorldFromEnv({ ...worldEnv });
|
|
611
|
-
const run = await world.runs.get(runId);
|
|
612
|
-
// Get original input/output
|
|
613
|
-
const hydratedRun = hydrate(run);
|
|
614
|
-
// Preserve original specVersion - if undefined (legacy v1), use SPEC_VERSION_LEGACY
|
|
615
|
-
const newRun = await start({ workflowId: run.workflowName }, hydratedRun.input, {
|
|
616
|
-
deploymentId: deploymentId ?? run.deploymentId,
|
|
617
|
-
world,
|
|
618
|
-
specVersion: run.specVersion ?? SPEC_VERSION_LEGACY,
|
|
619
|
-
});
|
|
620
|
-
return createResponse(newRun.runId);
|
|
621
|
-
}
|
|
622
|
-
catch (error) {
|
|
623
|
-
return createServerActionError(error, 'recreateRun', { runId });
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
/**
|
|
627
|
-
* Re-enqueue a workflow run.
|
|
628
|
-
*
|
|
629
|
-
* This re-enqueues the workflow orchestration layer. It's a no-op unless the workflow
|
|
630
|
-
* got stuck due to an implementation issue in the World. Useful for debugging custom Worlds.
|
|
631
|
-
*/
|
|
632
|
-
export async function reenqueueRun(worldEnv, runId) {
|
|
633
|
-
try {
|
|
634
|
-
const world = await getWorldFromEnv({ ...worldEnv });
|
|
635
|
-
const run = await world.runs.get(runId);
|
|
636
|
-
const deploymentId = run.deploymentId;
|
|
637
|
-
await world.queue(`__wkf_workflow_${run.workflowName}`, {
|
|
638
|
-
runId,
|
|
639
|
-
}, {
|
|
640
|
-
deploymentId,
|
|
641
|
-
});
|
|
642
|
-
return createResponse(undefined);
|
|
643
|
-
}
|
|
644
|
-
catch (error) {
|
|
645
|
-
return createServerActionError(error, 'reenqueueRun', { runId });
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
/**
|
|
649
|
-
* Wake up a workflow run by interrupting pending sleep() calls.
|
|
650
|
-
*
|
|
651
|
-
* This finds wait_created events without matching wait_completed events,
|
|
652
|
-
* creates wait_completed events for them, and then re-enqueues the run.
|
|
653
|
-
*
|
|
654
|
-
* @param worldEnv - Environment configuration for the World
|
|
655
|
-
* @param runId - The run ID to wake up
|
|
656
|
-
* @param options - Optional settings to narrow down targeting (specific correlation IDs)
|
|
657
|
-
*/
|
|
658
|
-
export async function wakeUpRun(worldEnv, runId, options) {
|
|
659
|
-
try {
|
|
660
|
-
const world = await getWorldFromEnv({ ...worldEnv });
|
|
661
|
-
const run = await world.runs.get(runId);
|
|
662
|
-
const deploymentId = run.deploymentId;
|
|
663
|
-
const compatMode = isLegacySpecVersion(run.specVersion);
|
|
664
|
-
// Fetch all events for the run
|
|
665
|
-
const eventsResult = await world.events.list({
|
|
666
|
-
runId,
|
|
667
|
-
pagination: { limit: 1000 },
|
|
668
|
-
resolveData: 'none',
|
|
669
|
-
});
|
|
670
|
-
// Find wait_created events without matching wait_completed events
|
|
671
|
-
const waitCreatedEvents = eventsResult.data.filter((e) => e.eventType === 'wait_created');
|
|
672
|
-
const waitCompletedCorrelationIds = new Set(eventsResult.data
|
|
673
|
-
.filter((e) => e.eventType === 'wait_completed')
|
|
674
|
-
.map((e) => e.correlationId));
|
|
675
|
-
let pendingWaits = waitCreatedEvents.filter((e) => !waitCompletedCorrelationIds.has(e.correlationId));
|
|
676
|
-
// If specific correlation IDs are provided, filter to only those
|
|
677
|
-
if (options?.correlationIds && options.correlationIds.length > 0) {
|
|
678
|
-
const targetCorrelationIds = new Set(options.correlationIds);
|
|
679
|
-
pendingWaits = pendingWaits.filter((e) => e.correlationId && targetCorrelationIds.has(e.correlationId));
|
|
680
|
-
}
|
|
681
|
-
// Create wait_completed events for each pending wait
|
|
682
|
-
for (const waitEvent of pendingWaits) {
|
|
683
|
-
if (waitEvent.correlationId) {
|
|
684
|
-
// For v2, include specVersion in event data; for v1Compat, it's not needed
|
|
685
|
-
const eventData = compatMode
|
|
686
|
-
? {
|
|
687
|
-
eventType: 'wait_completed',
|
|
688
|
-
correlationId: waitEvent.correlationId,
|
|
689
|
-
}
|
|
690
|
-
: {
|
|
691
|
-
eventType: 'wait_completed',
|
|
692
|
-
correlationId: waitEvent.correlationId,
|
|
693
|
-
specVersion: run.specVersion,
|
|
694
|
-
};
|
|
695
|
-
await world.events.create(runId, eventData, { v1Compat: compatMode });
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
// Re-enqueue the run to wake it up
|
|
699
|
-
if (pendingWaits.length > 0) {
|
|
700
|
-
await world.queue(`__wkf_workflow_${run.workflowName}`, {
|
|
701
|
-
runId,
|
|
702
|
-
}, {
|
|
703
|
-
deploymentId,
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
return createResponse({ stoppedCount: pendingWaits.length });
|
|
707
|
-
}
|
|
708
|
-
catch (error) {
|
|
709
|
-
return createServerActionError(error, 'wakeUpRun', {
|
|
710
|
-
runId,
|
|
711
|
-
correlationIds: options?.correlationIds,
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Resume a hook by sending a payload.
|
|
717
|
-
*
|
|
718
|
-
* This sends a payload to a hook identified by its token, which resumes
|
|
719
|
-
* the associated workflow run. The payload will be available as the return
|
|
720
|
-
* value of the `createHook()` call in the workflow.
|
|
721
|
-
*
|
|
722
|
-
* @param worldEnv - Environment configuration for the World
|
|
723
|
-
* @param token - The hook token
|
|
724
|
-
* @param payload - The JSON payload to send to the hook
|
|
725
|
-
*/
|
|
726
|
-
export async function resumeHook(worldEnv, token, payload) {
|
|
727
|
-
try {
|
|
728
|
-
// Initialize the world so resumeHookRuntime can access it
|
|
729
|
-
await getWorldFromEnv({ ...worldEnv });
|
|
730
|
-
const hook = await resumeHookRuntime(token, payload);
|
|
731
|
-
return createResponse({
|
|
732
|
-
hookId: hook.hookId,
|
|
733
|
-
runId: hook.runId,
|
|
734
|
-
});
|
|
735
|
-
}
|
|
736
|
-
catch (error) {
|
|
737
|
-
return createServerActionError(error, 'resumeHook', {
|
|
738
|
-
token,
|
|
739
|
-
});
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
export async function readStreamServerAction(env, streamId, startIndex) {
|
|
743
|
-
try {
|
|
744
|
-
const world = await getWorldFromEnv(env);
|
|
745
|
-
// We should probably use getRun().getReadable() instead, to make the UI
|
|
746
|
-
// more consistent with runtime behavior, and also expose a "replay" and "startIndex",
|
|
747
|
-
// feature, to allow for testing World behavior.
|
|
748
|
-
const stream = await world.readFromStream(streamId, startIndex);
|
|
749
|
-
const revivers = getExternalRevivers(globalThis, [], '');
|
|
750
|
-
const transform = getDeserializeStream(revivers);
|
|
751
|
-
return stream.pipeThrough(transform);
|
|
752
|
-
}
|
|
753
|
-
catch (error) {
|
|
754
|
-
const actionError = createServerActionError(error, 'world.readFromStream', {
|
|
755
|
-
streamId,
|
|
756
|
-
startIndex,
|
|
757
|
-
});
|
|
758
|
-
if (!actionError.success) {
|
|
759
|
-
return actionError.error;
|
|
760
|
-
}
|
|
761
|
-
// Shouldn't happen, this is just a type guard
|
|
762
|
-
throw new Error();
|
|
763
|
-
}
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* List all stream IDs for a run
|
|
767
|
-
*/
|
|
768
|
-
export async function fetchStreams(env, runId) {
|
|
769
|
-
try {
|
|
770
|
-
const world = await getWorldFromEnv(env);
|
|
771
|
-
const streams = await world.listStreamsByRunId(runId);
|
|
772
|
-
return createResponse(streams);
|
|
773
|
-
}
|
|
774
|
-
catch (error) {
|
|
775
|
-
return createServerActionError(error, 'world.listStreamsByRunId', {
|
|
776
|
-
runId,
|
|
777
|
-
});
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
/**
|
|
781
|
-
* Fetch the workflows manifest from the workflow route directory
|
|
782
|
-
* The manifest is generated at build time and contains static structure info about workflows
|
|
783
|
-
*
|
|
784
|
-
* Configuration priority:
|
|
785
|
-
* 1. WORKFLOW_MANIFEST_PATH - explicit path to the manifest file
|
|
786
|
-
* 2. Standard Next.js app router locations (app/.well-known/workflow/v1/manifest.json)
|
|
787
|
-
* 3. WORKFLOW_EMBEDDED_DATA_DIR - legacy data directory
|
|
788
|
-
*/
|
|
789
|
-
export async function fetchWorkflowsManifest(_worldEnv) {
|
|
790
|
-
const cwd = getObservabilityCwd();
|
|
791
|
-
// Helper to resolve path (absolute or relative to cwd)
|
|
792
|
-
const resolvePath = (p) => path.isAbsolute(p) ? p : path.join(cwd, p);
|
|
793
|
-
// Build list of paths to try, in priority order
|
|
794
|
-
const manifestPaths = [];
|
|
795
|
-
// 1. Explicit manifest path configuration (highest priority)
|
|
796
|
-
if (process.env.WORKFLOW_MANIFEST_PATH) {
|
|
797
|
-
manifestPaths.push(resolvePath(process.env.WORKFLOW_MANIFEST_PATH));
|
|
798
|
-
}
|
|
799
|
-
// 2. Standard Next.js app router locations
|
|
800
|
-
manifestPaths.push(path.join(cwd, 'app/.well-known/workflow/v1/manifest.json'), path.join(cwd, 'src/app/.well-known/workflow/v1/manifest.json'));
|
|
801
|
-
// 3. Legacy data directory locations
|
|
802
|
-
if (process.env.WORKFLOW_EMBEDDED_DATA_DIR) {
|
|
803
|
-
manifestPaths.push(path.join(resolvePath(process.env.WORKFLOW_EMBEDDED_DATA_DIR), 'manifest.json'));
|
|
804
|
-
}
|
|
805
|
-
// Try each path until we find the manifest
|
|
806
|
-
for (const manifestPath of manifestPaths) {
|
|
807
|
-
try {
|
|
808
|
-
const content = await fs.readFile(manifestPath, 'utf-8');
|
|
809
|
-
const manifest = JSON.parse(content);
|
|
810
|
-
return createResponse(manifest);
|
|
811
|
-
}
|
|
812
|
-
catch (_err) {
|
|
813
|
-
// Continue to next path
|
|
814
|
-
}
|
|
815
|
-
}
|
|
816
|
-
// If no manifest found, return an empty manifest
|
|
817
|
-
// This allows the UI to work without workflows graph data
|
|
818
|
-
return createResponse({
|
|
819
|
-
version: '1.0.0',
|
|
820
|
-
steps: {},
|
|
821
|
-
workflows: {},
|
|
822
|
-
});
|
|
823
|
-
}
|
|
824
|
-
/**
|
|
825
|
-
* Run a queue-based health check on a workflow endpoint.
|
|
826
|
-
*
|
|
827
|
-
* This sends a health check message through the Queue infrastructure,
|
|
828
|
-
* bypassing Vercel Deployment Protection. The endpoint processes the
|
|
829
|
-
* message and writes a response to a stream, which we then read to
|
|
830
|
-
* verify the endpoint is healthy.
|
|
831
|
-
*
|
|
832
|
-
* @param worldEnv - Environment configuration for the World
|
|
833
|
-
* @param endpoint - Which endpoint to check: 'workflow' or 'step'
|
|
834
|
-
* @param options - Optional configuration (timeout in ms)
|
|
835
|
-
*/
|
|
836
|
-
export async function runHealthCheck(worldEnv, endpoint, options) {
|
|
837
|
-
const startTime = Date.now();
|
|
838
|
-
try {
|
|
839
|
-
const world = await getWorldFromEnv(worldEnv);
|
|
840
|
-
const result = await healthCheck(world, endpoint, options);
|
|
841
|
-
const latencyMs = Date.now() - startTime;
|
|
842
|
-
return createResponse({
|
|
843
|
-
...result,
|
|
844
|
-
latencyMs,
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
catch (error) {
|
|
848
|
-
const latencyMs = Date.now() - startTime;
|
|
849
|
-
// For health check failures, we want to return success=true with healthy=false
|
|
850
|
-
// so the UI can display the error properly, rather than propagating the server
|
|
851
|
-
// action error. This allows the health check result to be parsed by the UI
|
|
852
|
-
// even when the endpoint is down or unreachable.
|
|
853
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
854
|
-
return createResponse({
|
|
855
|
-
healthy: false,
|
|
856
|
-
error: errorMessage,
|
|
857
|
-
latencyMs,
|
|
858
|
-
});
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
//# sourceMappingURL=workflow-server-actions.js.map
|