@revenexx/studio-integrations 0.1.3 → 0.2.0

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.
Files changed (180) hide show
  1. package/dist/module.json +1 -1
  2. package/dist/runtime/components/Integrations/CredentialCreateDialog.d.vue.ts +13 -0
  3. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue +279 -0
  4. package/dist/runtime/components/Integrations/CredentialCreateDialog.vue.d.ts +13 -0
  5. package/dist/runtime/components/Integrations/CredentialDetailDialog.d.vue.ts +20 -0
  6. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue +342 -0
  7. package/dist/runtime/components/Integrations/CredentialDetailDialog.vue.d.ts +20 -0
  8. package/dist/runtime/components/Integrations/CredentialLogo.d.vue.ts +14 -0
  9. package/dist/runtime/components/Integrations/CredentialLogo.vue +37 -0
  10. package/dist/runtime/components/Integrations/CredentialLogo.vue.d.ts +14 -0
  11. package/dist/runtime/components/Integrations/CredentialRevenexxSource.d.vue.ts +12 -0
  12. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue +364 -0
  13. package/dist/runtime/components/Integrations/CredentialRevenexxSource.vue.d.ts +12 -0
  14. package/dist/runtime/components/Integrations/CredentialTypePicker.d.vue.ts +15 -0
  15. package/dist/runtime/components/Integrations/CredentialTypePicker.vue +71 -0
  16. package/dist/runtime/components/Integrations/CredentialTypePicker.vue.d.ts +15 -0
  17. package/dist/runtime/components/Integrations/DeadLettersPanel.d.vue.ts +5 -0
  18. package/dist/runtime/components/Integrations/DeadLettersPanel.vue +151 -0
  19. package/dist/runtime/components/Integrations/DeadLettersPanel.vue.d.ts +5 -0
  20. package/dist/runtime/components/Integrations/EditorTip.d.vue.ts +17 -0
  21. package/dist/runtime/components/Integrations/EditorTip.vue +46 -0
  22. package/dist/runtime/components/Integrations/EditorTip.vue.d.ts +17 -0
  23. package/dist/runtime/components/Integrations/EventTypeCombobox.d.vue.ts +9 -0
  24. package/dist/runtime/components/Integrations/EventTypeCombobox.vue +64 -0
  25. package/dist/runtime/components/Integrations/EventTypeCombobox.vue.d.ts +9 -0
  26. package/dist/runtime/components/Integrations/ExpressionInput.d.vue.ts +19 -0
  27. package/dist/runtime/components/Integrations/ExpressionInput.vue +236 -0
  28. package/dist/runtime/components/Integrations/ExpressionInput.vue.d.ts +19 -0
  29. package/dist/runtime/components/Integrations/GroupFrameView.d.vue.ts +28 -0
  30. package/dist/runtime/components/Integrations/GroupFrameView.vue +222 -0
  31. package/dist/runtime/components/Integrations/GroupFrameView.vue.d.ts +28 -0
  32. package/dist/runtime/components/Integrations/NodeConfigDynamicField.d.vue.ts +14 -0
  33. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue +184 -0
  34. package/dist/runtime/components/Integrations/NodeConfigDynamicField.vue.d.ts +14 -0
  35. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.d.vue.ts +16 -0
  36. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue +102 -0
  37. package/dist/runtime/components/Integrations/NodeConfigDynamicSchema.vue.d.ts +16 -0
  38. package/dist/runtime/components/Integrations/NodeConfigField.d.vue.ts +23 -0
  39. package/dist/runtime/components/Integrations/NodeConfigField.vue +587 -0
  40. package/dist/runtime/components/Integrations/NodeConfigField.vue.d.ts +23 -0
  41. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.d.vue.ts +14 -0
  42. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue +80 -0
  43. package/dist/runtime/components/Integrations/NodeDynamicOutputsResolver.vue.d.ts +14 -0
  44. package/dist/runtime/components/Integrations/NodeInspector.d.vue.ts +22 -3
  45. package/dist/runtime/components/Integrations/NodeInspector.vue +1074 -294
  46. package/dist/runtime/components/Integrations/NodeInspector.vue.d.ts +22 -3
  47. package/dist/runtime/components/Integrations/NodeLogo.d.vue.ts +9 -0
  48. package/dist/runtime/components/Integrations/NodeLogo.vue +38 -0
  49. package/dist/runtime/components/Integrations/NodeLogo.vue.d.ts +9 -0
  50. package/dist/runtime/components/Integrations/NodePalette.d.vue.ts +22 -1
  51. package/dist/runtime/components/Integrations/NodePalette.vue +411 -90
  52. package/dist/runtime/components/Integrations/NodePalette.vue.d.ts +22 -1
  53. package/dist/runtime/components/Integrations/QueryBuilder.d.vue.ts +9 -0
  54. package/dist/runtime/components/Integrations/QueryBuilder.vue +239 -0
  55. package/dist/runtime/components/Integrations/QueryBuilder.vue.d.ts +9 -0
  56. package/dist/runtime/components/Integrations/{RunDetailDialog.d.vue.ts → RunActionsMenu.d.vue.ts} +3 -4
  57. package/dist/runtime/components/Integrations/RunActionsMenu.vue +86 -0
  58. package/dist/runtime/components/Integrations/{RunDetailDialog.vue.d.ts → RunActionsMenu.vue.d.ts} +3 -4
  59. package/dist/runtime/components/Integrations/RunDetailPanel.d.vue.ts +14 -0
  60. package/dist/runtime/components/Integrations/RunDetailPanel.vue +777 -0
  61. package/dist/runtime/components/Integrations/RunDetailPanel.vue.d.ts +14 -0
  62. package/dist/runtime/components/Integrations/RunPayloadInput.d.vue.ts +13 -0
  63. package/dist/runtime/components/Integrations/RunPayloadInput.vue +175 -0
  64. package/dist/runtime/components/Integrations/RunPayloadInput.vue.d.ts +13 -0
  65. package/dist/runtime/components/Integrations/RunResponseModal.d.vue.ts +27 -0
  66. package/dist/runtime/components/Integrations/RunResponseModal.vue +84 -0
  67. package/dist/runtime/components/Integrations/RunResponseModal.vue.d.ts +27 -0
  68. package/dist/runtime/components/Integrations/RunsTable.d.vue.ts +21 -0
  69. package/dist/runtime/components/Integrations/RunsTable.vue +114 -0
  70. package/dist/runtime/components/Integrations/RunsTable.vue.d.ts +21 -0
  71. package/dist/runtime/components/Integrations/SftpAccountImportDialog.d.vue.ts +11 -0
  72. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue +91 -0
  73. package/dist/runtime/components/Integrations/SftpAccountImportDialog.vue.d.ts +11 -0
  74. package/dist/runtime/components/Integrations/Sidebar.vue +1 -0
  75. package/dist/runtime/components/Integrations/StickyNoteView.d.vue.ts +28 -0
  76. package/dist/runtime/components/Integrations/StickyNoteView.vue +208 -0
  77. package/dist/runtime/components/Integrations/StickyNoteView.vue.d.ts +28 -0
  78. package/dist/runtime/components/Integrations/TemplateFlowPreview.d.vue.ts +29 -0
  79. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue +201 -0
  80. package/dist/runtime/components/Integrations/TemplateFlowPreview.vue.d.ts +29 -0
  81. package/dist/runtime/components/Integrations/WorkflowContextMenu.d.vue.ts +32 -0
  82. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue +138 -0
  83. package/dist/runtime/components/Integrations/WorkflowContextMenu.vue.d.ts +32 -0
  84. package/dist/runtime/components/Integrations/WorkflowCreateDialog.d.vue.ts +9 -0
  85. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue +322 -0
  86. package/dist/runtime/components/Integrations/WorkflowCreateDialog.vue.d.ts +9 -0
  87. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.d.vue.ts +18 -0
  88. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue +63 -0
  89. package/dist/runtime/components/Integrations/WorkflowDuplicateDialog.vue.d.ts +18 -0
  90. package/dist/runtime/components/Integrations/WorkflowEdgeView.d.vue.ts +12 -1
  91. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue +68 -6
  92. package/dist/runtime/components/Integrations/WorkflowEdgeView.vue.d.ts +12 -1
  93. package/dist/runtime/components/Integrations/WorkflowEditor.d.vue.ts +8 -1
  94. package/dist/runtime/components/Integrations/WorkflowEditor.vue +1424 -144
  95. package/dist/runtime/components/Integrations/WorkflowEditor.vue.d.ts +8 -1
  96. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.d.vue.ts +12 -0
  97. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue +417 -0
  98. package/dist/runtime/components/Integrations/WorkflowHistoryPanel.vue.d.ts +12 -0
  99. package/dist/runtime/components/Integrations/WorkflowIconPicker.d.vue.ts +10 -0
  100. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue +89 -0
  101. package/dist/runtime/components/Integrations/WorkflowIconPicker.vue.d.ts +10 -0
  102. package/dist/runtime/components/Integrations/WorkflowIconView.d.vue.ts +10 -0
  103. package/dist/runtime/components/Integrations/WorkflowIconView.vue +35 -0
  104. package/dist/runtime/components/Integrations/WorkflowIconView.vue.d.ts +10 -0
  105. package/dist/runtime/components/Integrations/WorkflowNodeView.d.vue.ts +5 -1
  106. package/dist/runtime/components/Integrations/WorkflowNodeView.vue +114 -84
  107. package/dist/runtime/components/Integrations/WorkflowNodeView.vue.d.ts +5 -1
  108. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.d.vue.ts +12 -0
  109. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue +654 -0
  110. package/dist/runtime/components/Integrations/WorkflowRunReplayPanel.vue.d.ts +12 -0
  111. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.d.vue.ts +36 -0
  112. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue +136 -0
  113. package/dist/runtime/components/Integrations/WorkflowSettingsDialog.vue.d.ts +36 -0
  114. package/dist/runtime/components/Integrations/WorkflowTagsInput.d.vue.ts +14 -0
  115. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue +63 -0
  116. package/dist/runtime/components/Integrations/WorkflowTagsInput.vue.d.ts +14 -0
  117. package/dist/runtime/components/Integrations/WorkflowTriggerView.vue +35 -15
  118. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.d.vue.ts +13 -0
  119. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue +265 -0
  120. package/dist/runtime/components/Integrations/WorkflowVariablesDialog.vue.d.ts +13 -0
  121. package/dist/runtime/components/Integrations/WorkflowsTable.d.vue.ts +4 -1
  122. package/dist/runtime/components/Integrations/WorkflowsTable.vue +122 -39
  123. package/dist/runtime/components/Integrations/WorkflowsTable.vue.d.ts +4 -1
  124. package/dist/runtime/components/Integrations/workflowLayout.d.ts +5 -0
  125. package/dist/runtime/components/Integrations/workflowLayout.js +5 -3
  126. package/dist/runtime/composables/integrationsApi.d.ts +6 -2
  127. package/dist/runtime/composables/integrationsApi.js +5 -4
  128. package/dist/runtime/composables/useCanvasShortcuts.d.ts +37 -0
  129. package/dist/runtime/composables/useCanvasShortcuts.js +62 -0
  130. package/dist/runtime/composables/useIntegrationsDeadLetters.d.ts +22 -0
  131. package/dist/runtime/composables/useIntegrationsDeadLetters.js +50 -0
  132. package/dist/runtime/composables/useIntegrationsNodes.d.ts +4 -0
  133. package/dist/runtime/composables/useIntegrationsNodes.js +6 -3
  134. package/dist/runtime/composables/useIntegrationsRevisions.d.ts +40 -0
  135. package/dist/runtime/composables/useIntegrationsRevisions.js +51 -0
  136. package/dist/runtime/composables/useIntegrationsRuns.d.ts +21 -1
  137. package/dist/runtime/composables/useIntegrationsRuns.js +92 -4
  138. package/dist/runtime/composables/useIntegrationsTemplates.d.ts +1 -0
  139. package/dist/runtime/composables/useIntegrationsTemplates.js +32 -4
  140. package/dist/runtime/composables/useIntegrationsWorkflows.d.ts +1 -0
  141. package/dist/runtime/composables/useIntegrationsWorkflows.js +57 -1
  142. package/dist/runtime/composables/useMiniMapLayout.d.ts +34 -0
  143. package/dist/runtime/composables/useMiniMapLayout.js +119 -0
  144. package/dist/runtime/composables/useNodeConfigResolve.d.ts +43 -0
  145. package/dist/runtime/composables/useNodeConfigResolve.js +123 -0
  146. package/dist/runtime/composables/useNodeDrag.d.ts +22 -0
  147. package/dist/runtime/composables/useNodeDrag.js +10 -0
  148. package/dist/runtime/composables/usePaletteLayout.d.ts +41 -0
  149. package/dist/runtime/composables/usePaletteLayout.js +113 -0
  150. package/dist/runtime/data/expressionEditor.d.ts +22 -0
  151. package/dist/runtime/data/expressionEditor.js +40 -0
  152. package/dist/runtime/data/integrationNodeCatalog.js +8 -8
  153. package/dist/runtime/data/nodeInputData.d.ts +37 -0
  154. package/dist/runtime/data/nodeInputData.js +62 -0
  155. package/dist/runtime/data/nodeRunData.d.ts +56 -0
  156. package/dist/runtime/data/nodeRunData.js +91 -0
  157. package/dist/runtime/data/payloadForm.d.ts +44 -0
  158. package/dist/runtime/data/payloadForm.js +126 -0
  159. package/dist/runtime/data/workflowIconCatalog.d.ts +34 -0
  160. package/dist/runtime/data/workflowIconCatalog.js +116 -0
  161. package/dist/runtime/pages/integrations/audit.vue +25 -35
  162. package/dist/runtime/pages/integrations/credentials.vue +117 -122
  163. package/dist/runtime/pages/integrations/index.vue +133 -36
  164. package/dist/runtime/pages/integrations/runs.vue +125 -75
  165. package/dist/runtime/pages/integrations/schedules.vue +5 -16
  166. package/dist/runtime/pages/integrations/secrets.vue +1 -3
  167. package/dist/runtime/pages/integrations/workflows/[id].vue +599 -194
  168. package/dist/runtime/pages/integrations/workflows/index.vue +10 -8
  169. package/dist/runtime/types/integrations.d.ts +190 -4
  170. package/dist/runtime/utils/credentialState.d.ts +28 -0
  171. package/dist/runtime/utils/credentialState.js +29 -0
  172. package/dist/runtime/utils/localize.d.ts +3 -1
  173. package/dist/runtime/utils/localize.js +1 -2
  174. package/dist/runtime/utils/statusStyle.d.ts +6 -0
  175. package/dist/runtime/utils/statusStyle.js +15 -0
  176. package/package.json +12 -2
  177. package/dist/runtime/components/Integrations/RunDetailDialog.vue +0 -169
  178. package/dist/runtime/pages/integrations/workflows/new.d.vue.ts +0 -2
  179. package/dist/runtime/pages/integrations/workflows/new.vue +0 -153
  180. package/dist/runtime/pages/integrations/workflows/new.vue.d.ts +0 -2
@@ -0,0 +1,37 @@
1
+ /**
2
+ * useCanvasShortcuts — document-level keyboard shortcuts for the workflow
3
+ * canvas, with editing-context scoping.
4
+ *
5
+ * Modeled on n8n's useKeybindings (editor-ui/src/app/composables/useKeybindings.ts)
6
+ * but dependency-free (no @vueuse) and with the platform-neutral `mod` token.
7
+ *
8
+ * Map keys:
9
+ * - tokens joined by '+': modifiers `mod` (⌘ on mac, Ctrl elsewhere), `shift`,
10
+ * `alt`, plus one key: a single character ('a', '1', '+'), or a named key
11
+ * ('enter', 'escape', 'tab', 'space', 'f2', 'delete', 'backspace',
12
+ * 'arrowup', ...). Case-insensitive.
13
+ * - alternatives via '|': 'f2|space' fires on either.
14
+ *
15
+ * Handlers are a function or `{ disabled: () => boolean, run: fn }` — a disabled
16
+ * handler lets the event fall through untouched (no preventDefault), so e.g.
17
+ * Enter still types a newline when nothing is selected.
18
+ *
19
+ * Scoping — a keydown is IGNORED entirely when:
20
+ * 1. `options.disabled` is truthy (the editor passes "config lightbox open").
21
+ * 2. The event target (or activeElement fallback) is INPUT / TEXTAREA / SELECT,
22
+ * is contenteditable, or sits inside a [contenteditable] ancestor.
23
+ * 3. The target or an ancestor carries `data-canvas-shortcuts-ignore`.
24
+ * 4. `event.defaultPrevented` — someone upstream already claimed the key.
25
+ *
26
+ * On a match: preventDefault() + stopPropagation(), then run. Non-matching
27
+ * combos are never touched, so browser shortcuts keep working.
28
+ */
29
+ import type { MaybeRefOrGetter } from 'vue';
30
+ export type ShortcutHandler = ((event: KeyboardEvent) => void) | {
31
+ disabled: () => boolean;
32
+ run: (event: KeyboardEvent) => void;
33
+ };
34
+ export type ShortcutMap = Record<string, ShortcutHandler>;
35
+ export declare function useCanvasShortcuts(keymap: MaybeRefOrGetter<ShortcutMap>, options?: {
36
+ disabled?: MaybeRefOrGetter<boolean>;
37
+ }): void;
@@ -0,0 +1,62 @@
1
+ import { computed, onBeforeUnmount, onMounted, toValue } from "vue";
2
+ const IS_MAC = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "");
3
+ function canonical(parts) {
4
+ return parts.map((p) => p.toLowerCase()).sort((a, b) => a.localeCompare(b)).join("+");
5
+ }
6
+ function normalizeShortcut(shortcut) {
7
+ if (shortcut.length === 1) return shortcut.toLowerCase();
8
+ return canonical(shortcut.split("+"));
9
+ }
10
+ function codeToKey(code) {
11
+ if (code.startsWith("Key")) return code.slice(3).toLowerCase();
12
+ if (code.startsWith("Digit")) return code.slice(5);
13
+ return code.toLowerCase();
14
+ }
15
+ function eventShortcuts(event) {
16
+ const mods = [];
17
+ if (event.shiftKey) mods.push("shift");
18
+ if (event.altKey) mods.push("alt");
19
+ if (IS_MAC ? event.metaKey : event.ctrlKey) mods.push("mod");
20
+ const key = event.key === " " ? "space" : event.key.toLowerCase();
21
+ const code = codeToKey(event.code ?? "");
22
+ return {
23
+ byKey: canonical([...mods, key]),
24
+ byCode: canonical([...mods, code])
25
+ };
26
+ }
27
+ function isEditingContext(event) {
28
+ const el = event.target instanceof Element ? event.target : document.activeElement;
29
+ if (!el) return false;
30
+ const tag = el.tagName;
31
+ if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT") return true;
32
+ if (el.isContentEditable) return true;
33
+ if (el.closest('[contenteditable=""],[contenteditable="true"]')) return true;
34
+ if (el.closest("[data-canvas-shortcuts-ignore]")) return true;
35
+ return false;
36
+ }
37
+ export function useCanvasShortcuts(keymap, options) {
38
+ const normalized = computed(() => {
39
+ const out = {};
40
+ for (const [combo, handler] of Object.entries(toValue(keymap))) {
41
+ for (const alt of combo.split("|")) {
42
+ out[normalizeShortcut(alt.trim())] = handler;
43
+ }
44
+ }
45
+ return out;
46
+ });
47
+ function onKeyDown(event) {
48
+ if (toValue(options?.disabled)) return;
49
+ if (event.defaultPrevented) return;
50
+ if (isEditingContext(event)) return;
51
+ const { byKey, byCode } = eventShortcuts(event);
52
+ const handler = normalized.value[byKey] ?? normalized.value[byCode];
53
+ if (!handler) return;
54
+ const run = typeof handler === "function" ? handler : handler.disabled() ? void 0 : handler.run;
55
+ if (!run) return;
56
+ event.preventDefault();
57
+ event.stopPropagation();
58
+ run(event);
59
+ }
60
+ onMounted(() => document.addEventListener("keydown", onKeyDown));
61
+ onBeforeUnmount(() => document.removeEventListener("keydown", onKeyDown));
62
+ }
@@ -0,0 +1,22 @@
1
+ import type { DeadLetterRow } from '../types/integrations.js';
2
+ /** A dead letter enriched with its workflow's name (global "Missed runs" view). */
3
+ export interface GlobalDeadLetterRow extends DeadLetterRow {
4
+ workflowName: string;
5
+ }
6
+ export declare function useIntegrationsDeadLetters(): {
7
+ list: (workflowId: number | string, opts?: {
8
+ page?: number;
9
+ perPage?: number;
10
+ }) => Promise<{
11
+ items: DeadLetterRow[];
12
+ total: number;
13
+ page: number;
14
+ lastPage: number;
15
+ }>;
16
+ replay: (id: number | string) => Promise<unknown>;
17
+ discard: (id: number | string) => Promise<unknown>;
18
+ loadGlobal: (force?: boolean) => Promise<GlobalDeadLetterRow[]>;
19
+ globalItems: any;
20
+ globalLoading: any;
21
+ pendingCount: import("vue").ComputedRef<any>;
22
+ };
@@ -0,0 +1,50 @@
1
+ import { computed } from "vue";
2
+ import { useIntegrationsApi } from "./integrationsApi.js";
3
+ export function useIntegrationsDeadLetters() {
4
+ const api = useIntegrationsApi();
5
+ async function list(workflowId, opts = {}) {
6
+ const page = opts.page ?? 1;
7
+ const perPage = opts.perPage ?? 25;
8
+ const res = await api.get(
9
+ `/workflows/${workflowId}/dead-letters?page=${page}&per_page=${perPage}`
10
+ );
11
+ return {
12
+ items: res?.data ?? [],
13
+ total: res?.meta?.total ?? (res?.data?.length ?? 0),
14
+ page: res?.meta?.current_page ?? page,
15
+ lastPage: res?.meta?.last_page ?? 1
16
+ };
17
+ }
18
+ const replay = (id) => api.post(`/dead-letters/${id}/replay`, {});
19
+ const discard = (id) => api.post(`/dead-letters/${id}/discard`, {});
20
+ const { workflows, load: loadWorkflows } = useIntegrationsWorkflows();
21
+ const globalItems = useState("integrations.deadletters.global", () => []);
22
+ const globalLoading = useState("integrations.deadletters.global.loading", () => false);
23
+ const globalLoaded = useState("integrations.deadletters.global.loaded", () => false);
24
+ const pendingCount = computed(() => globalItems.value.filter((i) => i.status === "pending").length);
25
+ async function loadGlobal(force = false) {
26
+ if (globalLoading.value) return globalItems.value;
27
+ if (globalLoaded.value && !force) return globalItems.value;
28
+ globalLoading.value = true;
29
+ try {
30
+ await loadWorkflows();
31
+ const MAX_PAGES = 20;
32
+ const per = await Promise.allSettled(workflows.value.map(async (wf) => {
33
+ const out = [];
34
+ let page = 1;
35
+ while (page <= MAX_PAGES) {
36
+ const res = await list(wf.id, { page, perPage: 100 });
37
+ out.push(...res.items.map((i) => ({ ...i, workflowName: wf.name })));
38
+ if (page >= res.lastPage) break;
39
+ page++;
40
+ }
41
+ return out;
42
+ }));
43
+ globalItems.value = per.flatMap((p) => p.status === "fulfilled" ? p.value : []).sort((a, b) => new Date(b.created_at ?? 0).getTime() - new Date(a.created_at ?? 0).getTime());
44
+ globalLoaded.value = true;
45
+ } finally {
46
+ globalLoading.value = false;
47
+ }
48
+ }
49
+ return { list, replay, discard, loadGlobal, globalItems, globalLoading, pendingCount };
50
+ }
@@ -1,6 +1,10 @@
1
+ import { findManifest } from '../data/integrationNodeCatalog.js';
2
+ import { resolveLocalized } from '../utils/localize.js';
1
3
  export declare function useIntegrationsNodes(): {
2
4
  loading: any;
3
5
  error: any;
4
6
  loaded: any;
5
7
  load: (force?: boolean) => Promise<void>;
8
+ findManifest: typeof findManifest;
9
+ resolveLocalized: typeof resolveLocalized;
6
10
  };
@@ -1,4 +1,5 @@
1
- import { applyNodeCatalog } from "../data/integrationNodeCatalog.js";
1
+ import { applyNodeCatalog, findManifest } from "../data/integrationNodeCatalog.js";
2
+ import { resolveLocalized } from "../utils/localize.js";
2
3
  import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
4
  export function useIntegrationsNodes() {
4
5
  const loading = useState("integrations.nodes.loading", () => false);
@@ -17,7 +18,9 @@ export function useIntegrationsNodes() {
17
18
  ...n.manifest,
18
19
  slug: n.manifest.slug ?? n.slug,
19
20
  version: n.manifest.version ?? n.version,
20
- name: n.manifest.name ?? n.name
21
+ name: n.manifest.name ?? n.name,
22
+ // Keep the source package — the palette groups app nodes by it.
23
+ __package: n.package ? { name: n.package.name, label: n.package.label ?? null } : void 0
21
24
  }));
22
25
  applyNodeCatalog(manifests);
23
26
  loaded.value = true;
@@ -28,5 +31,5 @@ export function useIntegrationsNodes() {
28
31
  loading.value = false;
29
32
  }
30
33
  }
31
- return { loading, error, loaded, load };
34
+ return { loading, error, loaded, load, findManifest, resolveLocalized };
32
35
  }
@@ -0,0 +1,40 @@
1
+ import type { WorkflowBlob } from '../types/integrations.js';
2
+ /**
3
+ * Workflow revision history (PO — `workflow_histories`): every blob-changing
4
+ * save snapshots the previous revision. These endpoints expose that history
5
+ * and restore an archived revision as a brand-new one.
6
+ *
7
+ * GET /workflows/{id}/revisions → metadata list (paginated, newest first)
8
+ * GET /workflows/{id}/revisions/{rev} → full snapshot incl. blob + triggers
9
+ * POST /workflows/{id}/revisions/{rev}/restore
10
+ */
11
+ export interface WorkflowRevisionMeta {
12
+ id: number;
13
+ revision: number;
14
+ name: string;
15
+ description: string | null;
16
+ active: boolean;
17
+ blob_definition_version: string;
18
+ created_at: string | null;
19
+ updated_at: string | null;
20
+ archived_at: string | null;
21
+ }
22
+ export interface WorkflowRevisionDetail extends WorkflowRevisionMeta {
23
+ /** Cockpit-shaped blob: triggers merged in, like useIntegrationsWorkflows.toCockpit. */
24
+ blob: WorkflowBlob;
25
+ }
26
+ export declare function useIntegrationsRevisions(): {
27
+ loading: import("vue").Ref<boolean, boolean>;
28
+ error: import("vue").Ref<string | null, string | null>;
29
+ list: (workflowId: number, page?: number, perPage?: number) => Promise<{
30
+ revisions: WorkflowRevisionMeta[];
31
+ meta: {
32
+ current_page: number;
33
+ per_page: number;
34
+ total: number;
35
+ last_page: number;
36
+ };
37
+ }>;
38
+ get: (workflowId: number, revision: number) => Promise<WorkflowRevisionDetail>;
39
+ restore: (workflowId: number, revision: number) => Promise<void>;
40
+ };
@@ -0,0 +1,51 @@
1
+ import { ref } from "vue";
2
+ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
+ export function useIntegrationsRevisions() {
4
+ const api = useIntegrationsApi();
5
+ const loading = ref(false);
6
+ const error = ref(null);
7
+ function captureError(e, fallback) {
8
+ if (e instanceof IntegrationsApiError) return e.message;
9
+ return e?.message ?? fallback;
10
+ }
11
+ async function list(workflowId, page = 1, perPage = 25) {
12
+ loading.value = true;
13
+ error.value = null;
14
+ try {
15
+ const res = await api.get(`/workflows/${workflowId}/revisions?page=${page}&per_page=${perPage}`);
16
+ return { revisions: res.data ?? [], meta: res.meta };
17
+ } catch (e) {
18
+ error.value = captureError(e, "Could not load the revision history.");
19
+ throw e;
20
+ } finally {
21
+ loading.value = false;
22
+ }
23
+ }
24
+ async function get(workflowId, revision) {
25
+ const res = await api.get(`/workflows/${workflowId}/revisions/${revision}`);
26
+ const apiBlob = res.blob ?? {};
27
+ const triggers = (res.triggers ?? []).map((t) => ({
28
+ id: t.handle ?? String(t.id ?? ""),
29
+ type: t.type,
30
+ name: typeof t.name === "string" ? t.name : t.name ?? void 0,
31
+ active: t.active,
32
+ config: t.config ?? {}
33
+ }));
34
+ return {
35
+ ...res,
36
+ blob: {
37
+ nodeManifestVersion: apiBlob.nodeManifestVersion ?? "v0-draft",
38
+ name: apiBlob.name ?? res.name,
39
+ nodes: apiBlob.nodes ?? [],
40
+ edges: apiBlob.edges ?? [],
41
+ triggerPositions: apiBlob.triggerPositions ?? {},
42
+ stickies: apiBlob.stickies ?? [],
43
+ triggers
44
+ }
45
+ };
46
+ }
47
+ async function restore(workflowId, revision) {
48
+ await api.post(`/workflows/${workflowId}/revisions/${revision}/restore`, {});
49
+ }
50
+ return { loading, error, list, get, restore };
51
+ }
@@ -1,4 +1,4 @@
1
- import type { RunStatus, WorkflowRun } from '../types/integrations.js';
1
+ import type { RunStatus, RunStepRow, WorkflowRun } from '../types/integrations.js';
2
2
  export declare function useIntegrationsRuns(): {
3
3
  runs: any;
4
4
  loading: any;
@@ -8,17 +8,37 @@ export declare function useIntegrationsRuns(): {
8
8
  failedCount: import("vue").ComputedRef<any>;
9
9
  runningCount: import("vue").ComputedRef<any>;
10
10
  load: (force?: boolean) => Promise<WorkflowRun[]>;
11
+ loadForWorkflow: (workflowId: number, workflowName: string, opts?: {
12
+ reconcile?: boolean;
13
+ page?: number;
14
+ perPage?: number;
15
+ }) => Promise<{
16
+ runs: WorkflowRun[];
17
+ total: number;
18
+ page: number;
19
+ lastPage: number;
20
+ }>;
11
21
  list: (filter?: {
12
22
  workflowId?: number;
13
23
  status?: RunStatus;
14
24
  }) => WorkflowRun[];
15
25
  get: (id: string) => WorkflowRun | null;
26
+ fetchRun: (workflowId: number | string, runId: number | string, workflowName: string) => Promise<WorkflowRun>;
16
27
  trigger: (workflowId: number | string, payload?: Record<string, unknown>, mode?: "sync" | "async") => Promise<unknown>;
28
+ cancelRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
29
+ terminateRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
30
+ retryRun: (workflowId: number | string, runId: number | string) => Promise<unknown>;
31
+ resumeRun: (workflowId: number | string, runId: number | string, reason?: string) => Promise<unknown>;
17
32
  getRun: (workflowId: number | string, runId: number | string) => Promise<unknown>;
18
33
  getDetails: (workflowId: number | string, runId: number | string) => Promise<{
19
34
  source: string;
20
35
  data: Record<string, unknown>;
21
36
  }>;
37
+ getSteps: (workflowId: number | string, runId: number | string, includePayloads?: boolean) => Promise<{
38
+ source: string;
39
+ run: Record<string, unknown>;
40
+ steps: RunStepRow[];
41
+ }>;
22
42
  getHistory: (workflowId: number | string, runId: number | string, params?: {
23
43
  page_token?: string;
24
44
  page_size?: number;
@@ -8,14 +8,18 @@ function mapStatus(status) {
8
8
  return "queued";
9
9
  case "terminated":
10
10
  return "failed";
11
+ case "timed_out":
12
+ return "failed";
11
13
  case "running":
12
14
  return "running";
13
15
  case "failed":
14
16
  return "failed";
15
17
  case "cancelled":
16
18
  return "cancelled";
19
+ // An unknown/unexpected status must never surface as a stuck `running` —
20
+ // fail closed so a run the backend can't classify is visibly terminal.
17
21
  default:
18
- return "running";
22
+ return "failed";
19
23
  }
20
24
  }
21
25
  export function useIntegrationsRuns() {
@@ -23,19 +27,25 @@ export function useIntegrationsRuns() {
23
27
  const loading = useState("integrations.runs.loading", () => false);
24
28
  const error = useState("integrations.runs.error", () => null);
25
29
  const loaded = useState("integrations.runs.loaded", () => false);
30
+ const reconciling = useState("integrations.runs.reconciling", () => false);
31
+ const SYNC_LIMIT = 5;
26
32
  const api = useIntegrationsApi();
27
33
  const { workflows, load: loadWorkflows } = useIntegrationsWorkflows();
28
34
  function mapRun(r, workflowName) {
35
+ const startedMs = r.started_at ? new Date(r.started_at).getTime() : null;
36
+ const finishedMs = r.completed_at ? new Date(r.completed_at).getTime() : null;
29
37
  return {
30
38
  id: String(r.id),
31
39
  workflowId: r.workflow_id,
32
40
  workflowName,
33
41
  status: mapStatus(r.status),
42
+ temporalRunId: r.temporal_run_id ?? null,
34
43
  triggerId: "",
35
44
  triggerType: "manual",
36
45
  startedAt: r.started_at ?? "",
37
46
  finishedAt: r.completed_at ?? null,
38
- durationMs: null,
47
+ // Derived from the timestamps — the list payload carries no duration.
48
+ durationMs: startedMs != null && finishedMs != null && finishedMs >= startedMs ? finishedMs - startedMs : null,
39
49
  steps: []
40
50
  };
41
51
  }
@@ -46,13 +56,41 @@ export function useIntegrationsRuns() {
46
56
  error.value = null;
47
57
  try {
48
58
  await loadWorkflows();
59
+ const MAX_PAGES = 100;
49
60
  const perWorkflow = await Promise.allSettled(
50
61
  workflows.value.map(async (wf) => {
51
- const res = await api.get(`/workflows/${wf.id}/runs`);
52
- return (res?.data ?? []).map((r) => mapRun(r, wf.name));
62
+ const out = [];
63
+ let page = 1;
64
+ while (page <= MAX_PAGES) {
65
+ const res = await api.get(
66
+ `/workflows/${wf.id}/runs?page=${page}&per_page=100`
67
+ );
68
+ out.push(...(res?.data ?? []).map((r) => mapRun(r, wf.name)));
69
+ const last = res?.meta?.last_page ?? 1;
70
+ if (page >= last) break;
71
+ page++;
72
+ }
73
+ return out;
53
74
  })
54
75
  );
55
76
  runs.value = perWorkflow.flatMap((p) => p.status === "fulfilled" ? p.value : []).sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
77
+ if (!reconciling.value) {
78
+ reconciling.value = true;
79
+ try {
80
+ const active = runs.value.filter((r) => r.status === "running" || r.status === "queued").sort((a, b) => Number(b.id) - Number(a.id)).slice(0, SYNC_LIMIT);
81
+ await Promise.allSettled(active.map(async (r) => {
82
+ try {
83
+ const raw = await api.get(`/workflows/${r.workflowId}/runs/${r.id}`);
84
+ const apiRun = raw?.data ?? raw;
85
+ const fresh = mapRun(apiRun, r.workflowName);
86
+ runs.value = runs.value.map((x) => x.id === fresh.id ? fresh : x);
87
+ } catch {
88
+ }
89
+ }));
90
+ } finally {
91
+ reconciling.value = false;
92
+ }
93
+ }
56
94
  loaded.value = true;
57
95
  } catch (e) {
58
96
  error.value = e instanceof IntegrationsApiError ? e.message : e?.message ?? "Could not load runs.";
@@ -61,6 +99,32 @@ export function useIntegrationsRuns() {
61
99
  }
62
100
  return runs.value;
63
101
  }
102
+ async function loadForWorkflow(workflowId, workflowName, opts = {}) {
103
+ const page = opts.page ?? 1;
104
+ const perPage = opts.perPage ?? 25;
105
+ const res = await api.get(
106
+ `/workflows/${workflowId}/runs?page=${page}&per_page=${perPage}`
107
+ );
108
+ let list2 = (res?.data ?? []).map((r) => mapRun(r, workflowName)).sort((a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime());
109
+ if (opts.reconcile) {
110
+ const active = list2.filter((r) => r.status === "running" || r.status === "queued").sort((a, b) => Number(b.id) - Number(a.id)).slice(0, SYNC_LIMIT);
111
+ await Promise.allSettled(active.map(async (r) => {
112
+ try {
113
+ const raw = await api.get(`/workflows/${workflowId}/runs/${r.id}`);
114
+ const apiRun = raw?.data ?? raw;
115
+ const fresh = mapRun(apiRun, workflowName);
116
+ list2 = list2.map((x) => x.id === fresh.id ? fresh : x);
117
+ } catch {
118
+ }
119
+ }));
120
+ }
121
+ return {
122
+ runs: list2,
123
+ total: res?.meta?.total ?? list2.length,
124
+ page: res?.meta?.current_page ?? page,
125
+ lastPage: res?.meta?.last_page ?? 1
126
+ };
127
+ }
64
128
  function list(filter) {
65
129
  let result = [...runs.value];
66
130
  if (filter?.workflowId !== void 0) result = result.filter((r) => r.workflowId === filter.workflowId);
@@ -76,8 +140,25 @@ export function useIntegrationsRuns() {
76
140
  const res = await api.post(`/workflows/${workflowId}/runs`, body);
77
141
  return res?.data ?? res;
78
142
  }
143
+ async function runAction(workflowId, runId, action, reason) {
144
+ const body = reason ? { reason } : {};
145
+ const res = await api.post(`/workflows/${workflowId}/runs/${runId}/${action}`, body);
146
+ return res?.data ?? res;
147
+ }
148
+ const cancelRun = (workflowId, runId, reason) => runAction(workflowId, runId, "cancel", reason);
149
+ const terminateRun = (workflowId, runId, reason) => runAction(workflowId, runId, "terminate", reason);
150
+ const retryRun = (workflowId, runId) => runAction(workflowId, runId, "retry");
151
+ const resumeRun = (workflowId, runId, reason) => runAction(workflowId, runId, "resume", reason);
79
152
  const getRun = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}`);
153
+ async function fetchRun(workflowId, runId, workflowName) {
154
+ const raw = await getRun(workflowId, runId);
155
+ const apiRun = raw?.data ?? raw;
156
+ return mapRun(apiRun, workflowName);
157
+ }
80
158
  const getDetails = (workflowId, runId) => api.get(`/workflows/${workflowId}/runs/${runId}/details`);
159
+ const getSteps = (workflowId, runId, includePayloads = false) => api.get(
160
+ `/workflows/${workflowId}/runs/${runId}/steps${includePayloads ? "?include_payloads=1" : ""}`
161
+ );
81
162
  const getHistory = (workflowId, runId, params = {}) => {
82
163
  const qs = new URLSearchParams();
83
164
  if (params.page_token) qs.set("page_token", params.page_token);
@@ -98,11 +179,18 @@ export function useIntegrationsRuns() {
98
179
  failedCount,
99
180
  runningCount,
100
181
  load,
182
+ loadForWorkflow,
101
183
  list,
102
184
  get,
185
+ fetchRun,
103
186
  trigger,
187
+ cancelRun,
188
+ terminateRun,
189
+ retryRun,
190
+ resumeRun,
104
191
  getRun,
105
192
  getDetails,
193
+ getSteps,
106
194
  getHistory,
107
195
  getStackTrace
108
196
  };
@@ -10,6 +10,7 @@ export declare function useIntegrationsTemplates(): {
10
10
  list: () => WorkflowTemplate[];
11
11
  get: (slug: string) => WorkflowTemplate | null;
12
12
  fetchDetail: (slug: string) => Promise<WorkflowTemplate | null>;
13
+ ensureDetails: (slugs: string[], concurrency?: number) => Promise<void>;
13
14
  requirements: (slug: string) => Promise<TemplateRequirements | null>;
14
15
  instantiate: (slug: string, opts?: {
15
16
  name?: string;
@@ -3,6 +3,14 @@ import { IntegrationsApiError, useIntegrationsApi } from "./integrationsApi.js";
3
3
  import { resolveLocalized } from "../utils/localize.js";
4
4
  const BLOB_VERSION = "v0-draft";
5
5
  const INDUSTRY_VALUES = ["mechanical-engineering", "chemicals", "wholesale", "manufacturing", "retail", "any"];
6
+ function plainExcerpt(md) {
7
+ for (const raw of md.split("\n")) {
8
+ const line = raw.trim();
9
+ if (!line || line.startsWith("#")) continue;
10
+ return line.replace(/\*\*([^*]+)\*\*/g, "$1").replace(/\*([^*]+)\*/g, "$1").replace(/`([^`]+)`/g, "$1").replace(/\[([^\]]+)\]\([^)]*\)/g, "$1");
11
+ }
12
+ return "";
13
+ }
6
14
  function publisherFor(slug, vendors) {
7
15
  const ns = slug.includes(":") ? slug.split(":")[0] : vendors[0] ?? "revenexx";
8
16
  const kind = ns === "revenexx" ? "revenexx" : "partner";
@@ -15,10 +23,16 @@ function emptyBlob(name) {
15
23
  return { nodeManifestVersion: BLOB_VERSION, name, nodes: [], edges: [], triggerPositions: {}, triggers: [] };
16
24
  }
17
25
  function mapListItem(api) {
26
+ const short = resolveLocalized(api.shortDescription ?? "");
27
+ const md = resolveLocalized(api.description ?? "");
18
28
  return {
19
29
  slug: api.slug,
20
30
  name: resolveLocalized(api.name),
21
- description: resolveLocalized(api.description || api.shortDescription || ""),
31
+ description: short || plainExcerpt(md),
32
+ descriptionMd: md,
33
+ triggerTypes: api.triggerTypes ?? [],
34
+ images: api.images ?? [],
35
+ package: api.package ? { name: api.package.name, label: api.package.label ?? null } : null,
22
36
  category: api.category ?? "sync",
23
37
  industries: mapIndustries(api.industries),
24
38
  vendors: api.vendors ?? [],
@@ -48,13 +62,17 @@ function applyDetail(base, api) {
48
62
  name: def.name ?? base.name,
49
63
  nodes,
50
64
  edges: def.edges ?? [],
51
- triggerPositions: {},
65
+ triggerPositions: def.triggerPositions ?? {},
66
+ variables: def.variables,
52
67
  triggers
53
68
  };
69
+ const md = resolveLocalized(api.description ?? "") || base.descriptionMd;
54
70
  return {
55
71
  ...base,
56
- description: resolveLocalized(api.description || api.shortDescription || "") || base.description,
72
+ description: base.description || plainExcerpt(md),
73
+ descriptionMd: md,
57
74
  blob,
75
+ detailLoaded: true,
58
76
  nodeCount: nodes.length,
59
77
  outline: nodes.map((n) => n.nodeSlug ?? n.id).filter(Boolean)
60
78
  };
@@ -107,6 +125,16 @@ export function useIntegrationsTemplates() {
107
125
  return base;
108
126
  }
109
127
  }
128
+ async function ensureDetails(slugs, concurrency = 4) {
129
+ const queue = slugs.filter((slug) => get(slug)?.fromApi && !get(slug)?.detailLoaded);
130
+ const workers = Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
131
+ while (queue.length) {
132
+ const slug = queue.shift();
133
+ if (slug) await fetchDetail(slug);
134
+ }
135
+ });
136
+ await Promise.all(workers);
137
+ }
110
138
  async function requirements(slug) {
111
139
  try {
112
140
  const raw = unwrap(await api.get(`/templates/${slug}/requirements`));
@@ -137,5 +165,5 @@ export function useIntegrationsTemplates() {
137
165
  }
138
166
  const total = computed(() => templates.value.length);
139
167
  const popular = computed(() => [...templates.value].sort((a, b) => b.installs - a.installs).slice(0, 4));
140
- return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, requirements, instantiate, install };
168
+ return { templates, loaded, loading, error, total, popular, load, list, get, fetchDetail, ensureDetails, requirements, instantiate, install };
141
169
  }
@@ -23,6 +23,7 @@ export declare function useIntegrationsWorkflows(): {
23
23
  active: boolean;
24
24
  executionMode?: WorkflowExecutionMode;
25
25
  }) => Promise<Workflow>;
26
+ duplicate: (id: number, name?: string) => Promise<Workflow>;
26
27
  remove: (id: number) => Promise<void>;
27
28
  toggleActive: (id: number) => Promise<Workflow | null>;
28
29
  };