orcheo-studio 0.24.0 → 0.24.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/.bumpversion.cfg +1 -1
- package/dist/assets/{arc-DAv23sxp.js → arc-DhTjQsml.js} +1 -1
- package/dist/assets/{architectureDiagram-VXUJARFQ-D_gUzQPQ.js → architectureDiagram-VXUJARFQ-CGDm4iVL.js} +1 -1
- package/dist/assets/{blockDiagram-VD42YOAC-2_hx47qT.js → blockDiagram-VD42YOAC-B5xlwwyL.js} +1 -1
- package/dist/assets/{c4Diagram-YG6GDRKO-dehdDKxT.js → c4Diagram-YG6GDRKO-DtVXFxFX.js} +1 -1
- package/dist/assets/channel-CeR8j9Si.js +1 -0
- package/dist/assets/{chunk-4BX2VUAB-CtLOuP9F.js → chunk-4BX2VUAB-CA8vZ6Tb.js} +1 -1
- package/dist/assets/{chunk-55IACEB6-CwinXV4-.js → chunk-55IACEB6-CxNWLhGM.js} +1 -1
- package/dist/assets/{chunk-B4BG7PRW-BI7iPFRv.js → chunk-B4BG7PRW-BW8UEmwi.js} +1 -1
- package/dist/assets/{chunk-DI55MBZ5-CNYNrYjW.js → chunk-DI55MBZ5-_6YQcHqR.js} +1 -1
- package/dist/assets/{chunk-FMBD7UC4-BFVTHaU-.js → chunk-FMBD7UC4-L-9FXYi4.js} +1 -1
- package/dist/assets/{chunk-QN33PNHL-xWy7sSv1.js → chunk-QN33PNHL-DZtaWvmZ.js} +1 -1
- package/dist/assets/{chunk-QZHKN3VN-Ds_R1QaP.js → chunk-QZHKN3VN-B3c4-un7.js} +1 -1
- package/dist/assets/{chunk-TZMSLE5B-D8g2OOzb.js → chunk-TZMSLE5B-DmyAt3KD.js} +1 -1
- package/dist/assets/classDiagram-2ON5EDUG-B2tKUrqn.js +1 -0
- package/dist/assets/classDiagram-v2-WZHVMYZB-B2tKUrqn.js +1 -0
- package/dist/assets/clone-BN-HxSe-.js +1 -0
- package/dist/assets/{cose-bilkent-S5V4N54A-DnkMvL-f.js → cose-bilkent-S5V4N54A-r3dremCG.js} +1 -1
- package/dist/assets/{dagre-6UL2VRFP-dpKYJ1gN.js → dagre-6UL2VRFP-C6AizynV.js} +1 -1
- package/dist/assets/{diagram-PSM6KHXK-BnFoR8YQ.js → diagram-PSM6KHXK-CKTAJrFm.js} +1 -1
- package/dist/assets/{diagram-QEK2KX5R-Brg1AGVu.js → diagram-QEK2KX5R-B4BLbqZ6.js} +1 -1
- package/dist/assets/{diagram-S2PKOQOG-Dhmom7Q4.js → diagram-S2PKOQOG-CLy2S4hE.js} +1 -1
- package/dist/assets/{erDiagram-Q2GNP2WA-BTez8fDL.js → erDiagram-Q2GNP2WA-DlemVcfS.js} +1 -1
- package/dist/assets/{flowDiagram-NV44I4VS-CVXmtc0D.js → flowDiagram-NV44I4VS-CpBPPeuN.js} +1 -1
- package/dist/assets/{ganttDiagram-JELNMOA3-CHbw3AtF.js → ganttDiagram-JELNMOA3-BY4cJPdg.js} +1 -1
- package/dist/assets/{gitGraphDiagram-V2S2FVAM-CaPZ1SUE.js → gitGraphDiagram-V2S2FVAM-D7RJFr3i.js} +1 -1
- package/dist/assets/{graph-DjLsaDIL.js → graph-CJZvtDnb.js} +1 -1
- package/dist/assets/index-CARUdnbv.js +795 -0
- package/dist/assets/index-DcCYJX3c.css +1 -0
- package/dist/assets/{infoDiagram-HS3SLOUP-D_9DW9VU.js → infoDiagram-HS3SLOUP-CcSzbqKG.js} +1 -1
- package/dist/assets/{isUndefined-BkijP54r.js → isUndefined-BbMiFvTc.js} +1 -1
- package/dist/assets/{journeyDiagram-XKPGCS4Q-xxRWc5wn.js → journeyDiagram-XKPGCS4Q-DTmP5k2H.js} +1 -1
- package/dist/assets/{kanban-definition-3W4ZIXB7-BCI2NbpB.js → kanban-definition-3W4ZIXB7-BSA4gDhk.js} +1 -1
- package/dist/assets/{layout-M840MAqW.js → layout-D3BxIqM1.js} +1 -1
- package/dist/assets/{linear--_gMRK53.js → linear-CemsbS51.js} +1 -1
- package/dist/assets/{min-D06s3WKk.js → min-DkRMnH62.js} +1 -1
- package/dist/assets/{mindmap-definition-VGOIOE7T-B1-ALKFj.js → mindmap-definition-VGOIOE7T-CNj0QY6e.js} +1 -1
- package/dist/assets/{pieDiagram-ADFJNKIX-76kjoNcf.js → pieDiagram-ADFJNKIX-CPQsqbb4.js} +1 -1
- package/dist/assets/{quadrantDiagram-AYHSOK5B-O_d3GTQl.js → quadrantDiagram-AYHSOK5B-CPX25nd3.js} +1 -1
- package/dist/assets/{requirementDiagram-UZGBJVZJ-CasSIIPr.js → requirementDiagram-UZGBJVZJ-7mxxRNiB.js} +1 -1
- package/dist/assets/{sankeyDiagram-TZEHDZUN-CL_kG7jD.js → sankeyDiagram-TZEHDZUN-CPhitUZ9.js} +1 -1
- package/dist/assets/{sequenceDiagram-WL72ISMW-RYnztN0k.js → sequenceDiagram-WL72ISMW-BwLMsC7F.js} +1 -1
- package/dist/assets/{stateDiagram-FKZM4ZOC-NHFLaLiz.js → stateDiagram-FKZM4ZOC-Crx9CUqD.js} +1 -1
- package/dist/assets/stateDiagram-v2-4FDKWEC3-elh6u9Fl.js +1 -0
- package/dist/assets/{timeline-definition-IT6M3QCI-BH1cZoua.js → timeline-definition-IT6M3QCI-DDh1Iim0.js} +1 -1
- package/dist/assets/{treemap-GDKQZRPO-QAQDPzuO.js → treemap-GDKQZRPO-Bc6lNY0z.js} +1 -1
- package/dist/assets/{xychartDiagram-PRI3JC2R-C3M4ZzCP.js → xychartDiagram-PRI3JC2R-BAGZwI3e.js} +1 -1
- package/dist/index.html +2 -2
- package/package.json +1 -1
- package/src/features/account/pages/service-tokens.tsx +13 -60
- package/src/features/workflow/components/panels/rjsf-cron-widget.tsx +421 -0
- package/src/features/workflow/components/panels/rjsf-theme.tsx +2 -0
- package/src/features/workflow/lib/workflow-storage-api.ts +103 -2
- package/src/features/workflow/lib/workflow-storage-helpers.ts +19 -0
- package/src/features/workflow/pages/workflow/components/workflow-config-sheet.tsx +26 -5
- package/src/features/workflow/pages/workflow/components/workflow-tab-content.tsx +64 -1
- package/src/features/workflow/pages/workflow/hooks/controller/build-layout-props.ts +5 -0
- package/src/lib/api.ts +2 -17
- package/dist/assets/channel-C3cH7p5A.js +0 -1
- package/dist/assets/classDiagram-2ON5EDUG-o6x5AEsV.js +0 -1
- package/dist/assets/classDiagram-v2-WZHVMYZB-o6x5AEsV.js +0 -1
- package/dist/assets/clone-CnJR7M_G.js +0 -1
- package/dist/assets/index-DSthAh3a.css +0 -1
- package/dist/assets/index-DlptXU2u.js +0 -795
- package/dist/assets/stateDiagram-v2-4FDKWEC3-3y5AUrwb.js +0 -1
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
WorkflowListenerMetricsResponse,
|
|
15
15
|
WorkflowCredentialReadinessResponse,
|
|
16
16
|
WorkflowPublishResponse,
|
|
17
|
+
WorkflowRunnableConfig,
|
|
17
18
|
CandidateUpdateNote,
|
|
18
19
|
} from "./workflow-storage.types";
|
|
19
20
|
|
|
@@ -43,6 +44,46 @@ const readText = async (response: Response): Promise<string> => {
|
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Extract a human-readable message from an error response body.
|
|
49
|
+
*
|
|
50
|
+
* FastAPI serializes errors as `{ "detail": ... }`, where `detail` may be a
|
|
51
|
+
* plain string, our structured `{ message, code }` object, or a list of
|
|
52
|
+
* validation errors. Surface the readable message instead of the raw JSON so
|
|
53
|
+
* error toasts stay legible; fall back to the original text otherwise.
|
|
54
|
+
*/
|
|
55
|
+
export const extractErrorMessage = (body: string): string => {
|
|
56
|
+
try {
|
|
57
|
+
const parsed = JSON.parse(body) as unknown;
|
|
58
|
+
const detail = (parsed as { detail?: unknown })?.detail ?? parsed;
|
|
59
|
+
|
|
60
|
+
if (typeof detail === "string") {
|
|
61
|
+
return detail;
|
|
62
|
+
}
|
|
63
|
+
if (detail && typeof detail === "object") {
|
|
64
|
+
const message = (detail as { message?: unknown }).message;
|
|
65
|
+
if (typeof message === "string") {
|
|
66
|
+
return message;
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(detail)) {
|
|
69
|
+
const messages = detail
|
|
70
|
+
.map((item) =>
|
|
71
|
+
item && typeof item === "object"
|
|
72
|
+
? (item as { msg?: unknown }).msg
|
|
73
|
+
: undefined,
|
|
74
|
+
)
|
|
75
|
+
.filter((msg): msg is string => typeof msg === "string");
|
|
76
|
+
if (messages.length > 0) {
|
|
77
|
+
return messages.join("; ");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch {
|
|
82
|
+
// Body was not JSON; fall through to the raw text.
|
|
83
|
+
}
|
|
84
|
+
return body;
|
|
85
|
+
};
|
|
86
|
+
|
|
46
87
|
export const request = async <T>(
|
|
47
88
|
path: string,
|
|
48
89
|
options: RequestOptions = {},
|
|
@@ -56,7 +97,8 @@ export const request = async <T>(
|
|
|
56
97
|
});
|
|
57
98
|
|
|
58
99
|
if (!response.ok) {
|
|
59
|
-
const
|
|
100
|
+
const body = await readText(response);
|
|
101
|
+
const detail = body ? extractErrorMessage(body) : response.statusText;
|
|
60
102
|
throw new ApiRequestError(detail, response.status);
|
|
61
103
|
}
|
|
62
104
|
|
|
@@ -454,6 +496,62 @@ export const extractCronConfigFromVersionGraph = (
|
|
|
454
496
|
return extractCronConfigFromGraphNodes(graph);
|
|
455
497
|
};
|
|
456
498
|
|
|
499
|
+
// Matches a value that is exactly a single `{{config.configurable.<name>}}`
|
|
500
|
+
// placeholder, optionally padded with whitespace inside the braces.
|
|
501
|
+
const CONFIGURABLE_TEMPLATE = /^\{\{\s*config\.configurable\.([^}\s.]+)\s*\}\}$/;
|
|
502
|
+
|
|
503
|
+
const resolveConfigurableTemplate = (
|
|
504
|
+
value: string | null | undefined,
|
|
505
|
+
configurable: Record<string, unknown> | undefined,
|
|
506
|
+
): string | null | undefined => {
|
|
507
|
+
if (typeof value !== "string") {
|
|
508
|
+
return value;
|
|
509
|
+
}
|
|
510
|
+
const match = CONFIGURABLE_TEMPLATE.exec(value.trim());
|
|
511
|
+
if (!match) {
|
|
512
|
+
return value;
|
|
513
|
+
}
|
|
514
|
+
const name = match[1];
|
|
515
|
+
const resolved = configurable?.[name];
|
|
516
|
+
if (typeof resolved !== "string") {
|
|
517
|
+
throw new Error(
|
|
518
|
+
`Cron trigger references '{{config.configurable.${name}}}' but the ` +
|
|
519
|
+
`workflow has no configurable value named '${name}'. Set a default ` +
|
|
520
|
+
`for it in the workflow config.`,
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
return resolved;
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
// Cron triggers may parametrize fields (e.g. `expression`) with
|
|
527
|
+
// `{{config.configurable.X}}` placeholders that the workflow runtime resolves
|
|
528
|
+
// from its `configurable` config. Scheduling needs the concrete value, so
|
|
529
|
+
// resolve any such placeholder against the version's configurable values
|
|
530
|
+
// before the payload is validated as a cron expression by the backend.
|
|
531
|
+
const resolveConfigurableCronConfig = (
|
|
532
|
+
config: CronTriggerConfig,
|
|
533
|
+
runnableConfig: WorkflowRunnableConfig | null | undefined,
|
|
534
|
+
): CronTriggerConfig => {
|
|
535
|
+
const configurable = runnableConfig?.configurable;
|
|
536
|
+
return {
|
|
537
|
+
...config,
|
|
538
|
+
expression:
|
|
539
|
+
resolveConfigurableTemplate(config.expression, configurable) ??
|
|
540
|
+
config.expression,
|
|
541
|
+
timezone: resolveConfigurableTemplate(config.timezone, configurable) as
|
|
542
|
+
| string
|
|
543
|
+
| undefined,
|
|
544
|
+
start_at: resolveConfigurableTemplate(config.start_at, configurable) as
|
|
545
|
+
| string
|
|
546
|
+
| null
|
|
547
|
+
| undefined,
|
|
548
|
+
end_at: resolveConfigurableTemplate(config.end_at, configurable) as
|
|
549
|
+
| string
|
|
550
|
+
| null
|
|
551
|
+
| undefined,
|
|
552
|
+
};
|
|
553
|
+
};
|
|
554
|
+
|
|
457
555
|
const normalizeTemplateCronConfig = (
|
|
458
556
|
config: CronTriggerConfig,
|
|
459
557
|
metadata: unknown,
|
|
@@ -562,7 +660,10 @@ export const scheduleWorkflowFromLatestVersion = async (
|
|
|
562
660
|
|
|
563
661
|
const cronConfigRaw = extractCronConfigFromVersionGraph(latest.graph);
|
|
564
662
|
const cronConfig = cronConfigRaw
|
|
565
|
-
? normalizeTemplateCronConfig(
|
|
663
|
+
? normalizeTemplateCronConfig(
|
|
664
|
+
resolveConfigurableCronConfig(cronConfigRaw, latest.runnable_config),
|
|
665
|
+
latest.metadata,
|
|
666
|
+
)
|
|
566
667
|
: null;
|
|
567
668
|
if (!cronConfig) {
|
|
568
669
|
return {
|
|
@@ -216,6 +216,7 @@ const parseWorkflowMetadata = (
|
|
|
216
216
|
): WorkflowVersionMetadata => {
|
|
217
217
|
const configurableSchemas = parseConfigurableSchemas(
|
|
218
218
|
isRecord(metadata) ? metadata.configurable_schema : undefined,
|
|
219
|
+
isRecord(metadata) ? metadata.configurable_schema_order : undefined,
|
|
219
220
|
);
|
|
220
221
|
const avatarEmoji = extractAvatarEmoji(metadata);
|
|
221
222
|
const candidateSource = extractCandidateSource(metadata);
|
|
@@ -330,6 +331,7 @@ const parseWorkflowMetadata = (
|
|
|
330
331
|
|
|
331
332
|
const parseConfigurableSchemas = (
|
|
332
333
|
value: unknown,
|
|
334
|
+
order: unknown,
|
|
333
335
|
): Record<string, RJSFSchema> | undefined => {
|
|
334
336
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
335
337
|
return undefined;
|
|
@@ -344,6 +346,23 @@ const parseConfigurableSchemas = (
|
|
|
344
346
|
return undefined;
|
|
345
347
|
}
|
|
346
348
|
|
|
349
|
+
// The schema object is persisted in a JSONB column that does not preserve key
|
|
350
|
+
// order, so reorder by the authored `configurable_schema_order` array when
|
|
351
|
+
// present. Keys missing from the order list keep their existing position at
|
|
352
|
+
// the end, keeping output stable for legacy versions without the array.
|
|
353
|
+
if (Array.isArray(order) && order.length > 0) {
|
|
354
|
+
const byKey = new Map(entries);
|
|
355
|
+
const orderedKeys = order.filter(
|
|
356
|
+
(key): key is string => typeof key === "string" && byKey.has(key),
|
|
357
|
+
);
|
|
358
|
+
const seen = new Set(orderedKeys);
|
|
359
|
+
const orderedEntries = [
|
|
360
|
+
...orderedKeys.map((key) => [key, byKey.get(key)] as const),
|
|
361
|
+
...entries.filter(([key]) => !seen.has(key)),
|
|
362
|
+
];
|
|
363
|
+
return Object.fromEntries(orderedEntries) as Record<string, RJSFSchema>;
|
|
364
|
+
}
|
|
365
|
+
|
|
347
366
|
return Object.fromEntries(entries) as Record<string, RJSFSchema>;
|
|
348
367
|
};
|
|
349
368
|
|
|
@@ -44,12 +44,28 @@ const baseWorkflowConfigSchema: RJSFSchema = {
|
|
|
44
44
|
},
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
const buildWorkflowConfigUiSchema = (
|
|
48
|
+
configurableSchemas?: Record<string, RJSFSchema>,
|
|
49
|
+
): UiSchema => {
|
|
50
|
+
// Opt any key declared with `"format": "cron"` into the visual cron widget.
|
|
51
|
+
const cronEntries: UiSchema = {};
|
|
52
|
+
for (const [key, schema] of Object.entries(configurableSchemas ?? {})) {
|
|
53
|
+
if (schema?.format === "cron") {
|
|
54
|
+
cronEntries[key] = { "ui:widget": "cron" };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
configurable: {
|
|
60
|
+
// Render configurable fields in the order declared by config.json,
|
|
61
|
+
// with any data-only keys appended afterwards via the "*" wildcard.
|
|
62
|
+
"ui:order": [...Object.keys(configurableSchemas ?? {}), "*"],
|
|
63
|
+
fields: {
|
|
64
|
+
"ui:field": "jsonObject",
|
|
65
|
+
},
|
|
66
|
+
...cronEntries,
|
|
51
67
|
},
|
|
52
|
-
}
|
|
68
|
+
};
|
|
53
69
|
};
|
|
54
70
|
|
|
55
71
|
interface WorkflowConfigSheetProps {
|
|
@@ -91,6 +107,11 @@ export function WorkflowConfigSheet({
|
|
|
91
107
|
};
|
|
92
108
|
}, [formData.configurable, configurableSchemas]);
|
|
93
109
|
|
|
110
|
+
const workflowConfigUiSchema = useMemo<UiSchema>(
|
|
111
|
+
() => buildWorkflowConfigUiSchema(configurableSchemas),
|
|
112
|
+
[configurableSchemas],
|
|
113
|
+
);
|
|
114
|
+
|
|
94
115
|
useEffect(() => {
|
|
95
116
|
if (open) {
|
|
96
117
|
setFormData(toFormData(initialConfig));
|
|
@@ -2,12 +2,14 @@ import { useEffect, useMemo, useState } from "react";
|
|
|
2
2
|
import { useUploadsAllowed } from "@/hooks/use-uploads-allowed";
|
|
3
3
|
import {
|
|
4
4
|
AlertTriangle,
|
|
5
|
+
CheckCircle2,
|
|
5
6
|
Copy,
|
|
6
7
|
ExternalLink,
|
|
7
8
|
LoaderCircle,
|
|
8
9
|
Play,
|
|
9
10
|
RefreshCw,
|
|
10
11
|
UserMinus,
|
|
12
|
+
XCircle,
|
|
11
13
|
} from "lucide-react";
|
|
12
14
|
import { useNavigate } from "react-router-dom";
|
|
13
15
|
import { Controls, ReactFlow, type Node, type NodeProps } from "@xyflow/react";
|
|
@@ -56,6 +58,7 @@ import {
|
|
|
56
58
|
} from "@features/workflow/lib/mermaid-renderer";
|
|
57
59
|
import { resolveWorkflowVersionMermaidSource } from "@features/workflow/lib/workflow-storage-helpers";
|
|
58
60
|
import { WorkflowConfigSheet } from "@features/workflow/pages/workflow/components/workflow-config-sheet";
|
|
61
|
+
import type { WorkflowExecutionStatus } from "@features/workflow/pages/workflow/helpers/types";
|
|
59
62
|
|
|
60
63
|
export interface WorkflowTabContentProps {
|
|
61
64
|
workflowId: string | null;
|
|
@@ -66,6 +69,7 @@ export interface WorkflowTabContentProps {
|
|
|
66
69
|
loadError: string | null;
|
|
67
70
|
isRunPending: boolean;
|
|
68
71
|
isRunning: boolean;
|
|
72
|
+
lastRunStatus?: WorkflowExecutionStatus | null;
|
|
69
73
|
onRunWorkflow: () => Promise<void>;
|
|
70
74
|
onSaveConfig: (nextConfig: WorkflowRunnableConfig | null) => Promise<void>;
|
|
71
75
|
hasCronTriggerNode: boolean;
|
|
@@ -189,6 +193,49 @@ const getErrorMessage = (error: unknown, fallback: string): string => {
|
|
|
189
193
|
return rawMessage;
|
|
190
194
|
};
|
|
191
195
|
|
|
196
|
+
interface RunResultBanner {
|
|
197
|
+
Icon: typeof CheckCircle2;
|
|
198
|
+
className: string;
|
|
199
|
+
message: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const resolveRunResultBanner = (
|
|
203
|
+
status: WorkflowExecutionStatus,
|
|
204
|
+
): RunResultBanner => {
|
|
205
|
+
switch (status) {
|
|
206
|
+
case "success":
|
|
207
|
+
return {
|
|
208
|
+
Icon: CheckCircle2,
|
|
209
|
+
className:
|
|
210
|
+
"border-emerald-500/40 bg-emerald-50 text-emerald-700 dark:bg-emerald-950/40 dark:text-emerald-300",
|
|
211
|
+
message: "Workflow run succeeded. See the full trace on the Trace tab.",
|
|
212
|
+
};
|
|
213
|
+
case "failed":
|
|
214
|
+
return {
|
|
215
|
+
Icon: XCircle,
|
|
216
|
+
className: "border-destructive/40 bg-destructive/5 text-destructive",
|
|
217
|
+
message: "Workflow run failed. Check the Trace tab for details.",
|
|
218
|
+
};
|
|
219
|
+
case "partial":
|
|
220
|
+
return {
|
|
221
|
+
Icon: AlertTriangle,
|
|
222
|
+
className:
|
|
223
|
+
"border-amber-500/40 bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300",
|
|
224
|
+
message:
|
|
225
|
+
"Workflow run finished with partial results. Check the Trace tab for details.",
|
|
226
|
+
};
|
|
227
|
+
default:
|
|
228
|
+
// "running" reaching here means the stream ended without a final status.
|
|
229
|
+
return {
|
|
230
|
+
Icon: AlertTriangle,
|
|
231
|
+
className:
|
|
232
|
+
"border-amber-500/40 bg-amber-50 text-amber-700 dark:bg-amber-950/40 dark:text-amber-300",
|
|
233
|
+
message:
|
|
234
|
+
"Workflow run ended without a final status. Check the Trace tab for the latest status.",
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
192
239
|
export function WorkflowTabContent({
|
|
193
240
|
workflowId,
|
|
194
241
|
workflowName,
|
|
@@ -197,6 +244,7 @@ export function WorkflowTabContent({
|
|
|
197
244
|
loadError,
|
|
198
245
|
isRunPending,
|
|
199
246
|
isRunning,
|
|
247
|
+
lastRunStatus = null,
|
|
200
248
|
onRunWorkflow,
|
|
201
249
|
onSaveConfig,
|
|
202
250
|
hasCronTriggerNode,
|
|
@@ -390,6 +438,11 @@ export function WorkflowTabContent({
|
|
|
390
438
|
}
|
|
391
439
|
|
|
392
440
|
const canConfigure = Boolean(workflowId);
|
|
441
|
+
const isRunActive = isRunPending || isRunning;
|
|
442
|
+
const runResultBanner =
|
|
443
|
+
!isRunActive && lastRunStatus
|
|
444
|
+
? resolveRunResultBanner(lastRunStatus)
|
|
445
|
+
: null;
|
|
393
446
|
const canRun = Boolean(workflowId && latestVersion);
|
|
394
447
|
const runButtonDisabled = !canRun || isRunPending || isRunning;
|
|
395
448
|
const runButtonLabel = isRunPending || isRunning ? "Running..." : "Run";
|
|
@@ -667,7 +720,7 @@ export function WorkflowTabContent({
|
|
|
667
720
|
</div>
|
|
668
721
|
</div>
|
|
669
722
|
|
|
670
|
-
{
|
|
723
|
+
{isRunActive && (
|
|
671
724
|
<div
|
|
672
725
|
role="status"
|
|
673
726
|
className="rounded-md border border-primary/30 bg-primary/5 px-3 py-2 text-sm text-primary"
|
|
@@ -677,6 +730,16 @@ export function WorkflowTabContent({
|
|
|
677
730
|
</div>
|
|
678
731
|
)}
|
|
679
732
|
|
|
733
|
+
{runResultBanner && (
|
|
734
|
+
<div
|
|
735
|
+
role="status"
|
|
736
|
+
className={`flex items-center gap-2 rounded-md border px-3 py-2 text-sm ${runResultBanner.className}`}
|
|
737
|
+
>
|
|
738
|
+
<runResultBanner.Icon className="h-4 w-4 shrink-0" />
|
|
739
|
+
<span>{runResultBanner.message}</span>
|
|
740
|
+
</div>
|
|
741
|
+
)}
|
|
742
|
+
|
|
680
743
|
{isPublished && shareUrl && (
|
|
681
744
|
<div className="flex items-center justify-between rounded-md border border-border/60 bg-muted/20 px-3 py-2">
|
|
682
745
|
<div className="min-w-0">
|
|
@@ -75,6 +75,10 @@ export function buildWorkflowLayoutProps(
|
|
|
75
75
|
resources: WorkflowResources,
|
|
76
76
|
execution: WorkflowExecutionController,
|
|
77
77
|
): WorkflowLayoutProps {
|
|
78
|
+
const activeExecution = core.execution.executions.find(
|
|
79
|
+
(execution) => execution.id === core.execution.activeExecutionId,
|
|
80
|
+
);
|
|
81
|
+
|
|
78
82
|
const workflowProps: WorkflowTabContentProps = {
|
|
79
83
|
workflowId: core.metadata.currentWorkflowId,
|
|
80
84
|
workflowRouteRef: core.routeWorkflowRef,
|
|
@@ -84,6 +88,7 @@ export function buildWorkflowLayoutProps(
|
|
|
84
88
|
loadError: core.metadata.workflowLoadError,
|
|
85
89
|
isRunPending: execution.isRunPending,
|
|
86
90
|
isRunning: core.execution.isRunning,
|
|
91
|
+
lastRunStatus: activeExecution?.status ?? null,
|
|
87
92
|
onRunWorkflow: execution.handleRunPersistedWorkflow,
|
|
88
93
|
onSaveConfig: resources.saver.handleSaveWorkflowConfig,
|
|
89
94
|
hasCronTriggerNode: hasSchedulableCronTrigger(
|
package/src/lib/api.ts
CHANGED
|
@@ -405,6 +405,7 @@ export async function acceptWorkspaceInvitation(
|
|
|
405
405
|
|
|
406
406
|
export interface ServiceToken {
|
|
407
407
|
identifier: string;
|
|
408
|
+
name?: string | null;
|
|
408
409
|
secret?: string | null;
|
|
409
410
|
secret_preview?: string | null;
|
|
410
411
|
scopes: string[];
|
|
@@ -425,7 +426,7 @@ export interface ServiceTokenListResponse {
|
|
|
425
426
|
}
|
|
426
427
|
|
|
427
428
|
export interface CreateServiceTokenRequest {
|
|
428
|
-
|
|
429
|
+
name?: string;
|
|
429
430
|
scopes?: string[];
|
|
430
431
|
expires_in_seconds?: number | null;
|
|
431
432
|
}
|
|
@@ -504,22 +505,6 @@ export async function createServiceToken(
|
|
|
504
505
|
);
|
|
505
506
|
}
|
|
506
507
|
|
|
507
|
-
export async function rotateServiceToken(
|
|
508
|
-
tokenId: string,
|
|
509
|
-
overlapSeconds: number,
|
|
510
|
-
baseUrl?: string,
|
|
511
|
-
): Promise<ServiceToken> {
|
|
512
|
-
return serviceTokenRequest<ServiceToken>(
|
|
513
|
-
`/api/admin/service-tokens/${encodeURIComponent(tokenId)}/rotate`,
|
|
514
|
-
{
|
|
515
|
-
method: "POST",
|
|
516
|
-
body: JSON.stringify({ overlap_seconds: overlapSeconds }),
|
|
517
|
-
},
|
|
518
|
-
"Failed to rotate service token",
|
|
519
|
-
baseUrl,
|
|
520
|
-
);
|
|
521
|
-
}
|
|
522
|
-
|
|
523
508
|
export async function revokeServiceToken(
|
|
524
509
|
tokenId: string,
|
|
525
510
|
reason: string,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ap as o,aq as n}from"./index-DlptXU2u.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-BI7iPFRv.js";import{_ as i}from"./index-DlptXU2u.js";import"./chunk-FMBD7UC4-BFVTHaU-.js";import"./chunk-55IACEB6-CwinXV4-.js";import"./chunk-QN33PNHL-xWy7sSv1.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{s as a,c as s,a as e,C as t}from"./chunk-B4BG7PRW-BI7iPFRv.js";import{_ as i}from"./index-DlptXU2u.js";import"./chunk-FMBD7UC4-BFVTHaU-.js";import"./chunk-55IACEB6-CwinXV4-.js";import"./chunk-QN33PNHL-xWy7sSv1.js";var p={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{p as diagram};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{ay as r}from"./index-DlptXU2u.js";var a=4;function n(o){return r(o,a)}export{n as c};
|