@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
@@ -2,11 +2,13 @@
2
2
  import { AddCircle as Plus } from "#solar-icons";
3
3
  import { Button } from "@revenexx/studio";
4
4
  import WorkflowsTable from "../../../components/Integrations/WorkflowsTable.vue";
5
+ import WorkflowCreateDialog from "../../../components/Integrations/WorkflowCreateDialog.vue";
5
6
  useHead({ title: "Workflows \xB7 Revenexx Cockpit" });
6
- const { workflows, load } = useIntegrationsWorkflows();
7
+ const { workflows, loading, load } = useIntegrationsWorkflows();
7
8
  onMounted(() => {
8
9
  void load();
9
10
  });
11
+ const createOpen = ref(false);
10
12
  </script>
11
13
 
12
14
  <template>
@@ -15,17 +17,17 @@ onMounted(() => {
15
17
  title="Workflows"
16
18
  >
17
19
  <template #actions>
18
- <NuxtLink to="/integrations/workflows/new">
19
- <Button class="h-7 text-sm">
20
- <Plus :size="14" />
21
- New workflow
22
- </Button>
23
- </NuxtLink>
20
+ <Button class="h-7 text-sm" @click="createOpen = true">
21
+ <Plus :size="14" />
22
+ New workflow
23
+ </Button>
24
24
  </template>
25
25
  </PageHeader>
26
26
 
27
27
  <div class="p-4">
28
- <WorkflowsTable :workflows="workflows" />
28
+ <WorkflowsTable :workflows="workflows" :loading="loading" />
29
29
  </div>
30
+
31
+ <WorkflowCreateDialog v-model:open="createOpen" />
30
32
  </IntegrationsLayout>
31
33
  </template>
@@ -37,6 +37,12 @@ export interface WorkflowNodeRetry {
37
37
  backoffSeconds?: number;
38
38
  strategy?: RetryStrategy;
39
39
  }
40
+ /**
41
+ * What the engine does when a node still fails after its retries: `fail`
42
+ * (default) fails the run; `route` records the failure as the node's `error`
43
+ * output so a wired error edge handles it. Stop and Error always fails.
44
+ */
45
+ export type WorkflowNodeOnError = 'route' | 'fail';
40
46
  export interface WorkflowNodePosition {
41
47
  x: number;
42
48
  y: number;
@@ -48,10 +54,19 @@ export interface WorkflowNode {
48
54
  /** semver pin for the chosen manifest version. */
49
55
  nodeVersion: string;
50
56
  name?: LocalizedString;
57
+ /** Optional editor-only note/comment on this node instance (blob `nodes[].notes`). */
58
+ notes?: string;
51
59
  /** Free-form key→value config, validated against the node manifest. */
52
60
  config: Record<string, unknown>;
53
61
  position?: WorkflowNodePosition;
54
62
  retry?: WorkflowNodeRetry;
63
+ onError?: WorkflowNodeOnError;
64
+ /**
65
+ * Per-node execution timeout in seconds (engine start-to-close, 1..7200).
66
+ * Absent = engine default (10 minutes). For long-running orchestration
67
+ * nodes, e.g. waiting on a bulk import job.
68
+ */
69
+ timeoutSeconds?: number;
55
70
  }
56
71
  export interface WorkflowEdge {
57
72
  id: string;
@@ -64,6 +79,49 @@ export interface WorkflowEdge {
64
79
  input?: string;
65
80
  };
66
81
  }
82
+ /** Editor-only node group: a canvas frame owning member nodes (blob `groups[]`). */
83
+ export interface WorkflowGroup {
84
+ id: string;
85
+ label: LocalizedString;
86
+ /** Member node/trigger handles. */
87
+ nodeIds: string[];
88
+ /** Palette index 0..6 (shares the sticky palette). */
89
+ color?: number;
90
+ /** Collapsed groups render as one compact card; members hide. */
91
+ collapsed?: boolean;
92
+ /** Manually resized frame; absent = derived from the members' bounding box. */
93
+ rect?: {
94
+ x: number;
95
+ y: number;
96
+ width: number;
97
+ height: number;
98
+ };
99
+ }
100
+ /** Editor-only sticky note on the canvas (engine-irrelevant; blob `stickies[]`). */
101
+ export interface WorkflowSticky {
102
+ id: string;
103
+ /** Note text; markdown-lite (`#`/`##`/`###` headings, `**bold**`), line breaks kept. */
104
+ content: string;
105
+ position: {
106
+ x: number;
107
+ y: number;
108
+ };
109
+ /** Absent → the editor's 240x160 default. */
110
+ size?: {
111
+ width: number;
112
+ height: number;
113
+ };
114
+ /** Palette index 0..6 (0 = amber default). */
115
+ color?: number;
116
+ }
117
+ /** Editor-only workflow icon (BlobDef `icon`): a curated solar icon name or a node-package image URL. */
118
+ export interface WorkflowIcon {
119
+ kind: 'solar' | 'image';
120
+ /** Icon-library name (kind=solar). */
121
+ name?: string;
122
+ /** Image URL, e.g. a node-package logo (kind=image). */
123
+ src?: string;
124
+ }
67
125
  export interface WorkflowBlob {
68
126
  nodeManifestVersion: string;
69
127
  name: LocalizedString;
@@ -77,8 +135,22 @@ export interface WorkflowBlob {
77
135
  x: number;
78
136
  y: number;
79
137
  }>;
138
+ /** Canvas sticky notes (engine-irrelevant annotations, BlobDef `stickies`). */
139
+ stickies?: WorkflowSticky[];
140
+ /** Canvas node groups (engine-irrelevant frames, BlobDef `groups`). */
141
+ groups?: WorkflowGroup[];
142
+ /** Organisational tags for grouping/filtering (engine-irrelevant, BlobDef `tags`). */
143
+ tags?: string[];
144
+ /** Workflow icon shown in lists and headers (engine-irrelevant, BlobDef `icon`). */
145
+ icon?: WorkflowIcon;
80
146
  }
81
147
  export type WorkflowExecutionMode = 'async_only' | 'sync_only' | 'caller_decides';
148
+ /**
149
+ * Build state of a workflow's executable bundle (backend `build_status`). Source
150
+ * of truth for runnability — the worker pool only runs a workflow once its
151
+ * bundle is `ready`; the API rejects runs with 409 in any other state.
152
+ */
153
+ export type WorkflowBuildStatus = 'pending' | 'building' | 'ready' | 'failed';
82
154
  export interface Workflow {
83
155
  id: number;
84
156
  name: string;
@@ -88,6 +160,10 @@ export interface Workflow {
88
160
  active: boolean;
89
161
  revision: number;
90
162
  execution_mode?: WorkflowExecutionMode;
163
+ /** Bundle build state; `null` on legacy rows the builder never touched. */
164
+ buildStatus: WorkflowBuildStatus | null;
165
+ /** Human-readable build failure reason, present when `buildStatus === 'failed'`. */
166
+ buildError: string | null;
91
167
  created_at: string;
92
168
  updated_at: string;
93
169
  }
@@ -106,12 +182,18 @@ export interface NodeManifestOutput {
106
182
  dataType: NodeDataType;
107
183
  description?: LocalizedString;
108
184
  sourceFromConfig?: string;
185
+ /** When true, the node's outputs for this manifest are resolved dynamically
186
+ * via config:resolve with target 'outputs' (later phase; harmless now). */
187
+ resolveOutputs?: boolean;
109
188
  fallback?: {
110
189
  name: string;
111
190
  label?: LocalizedString;
112
191
  };
192
+ /** Declared field names of this output's payload, when the manifest
193
+ * publishes them. Author-time schema only, never live run data. */
194
+ fields?: string[] | Record<string, unknown>;
113
195
  }
114
- export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref';
196
+ export type ConfigFieldType = 'string' | 'number' | 'boolean' | 'select' | 'multiselect' | 'object' | 'array' | 'expression' | 'secret-ref' | 'credentials-ref' | 'dynamic-schema';
115
197
  export interface ConfigFieldOption {
116
198
  value: string | number | boolean;
117
199
  label: LocalizedString;
@@ -126,12 +208,20 @@ export interface ConfigField {
126
208
  placeholder?: LocalizedString;
127
209
  expressionAllowed?: boolean;
128
210
  options?: ConfigFieldOption[];
211
+ /** When true, this field's `options` are resolved at author-time via
212
+ * config:resolve with target === this field's `key` (see useNodeConfigResolve). */
213
+ dynamic?: boolean;
129
214
  /** sub-fields when type === 'object' (one level deep) */
130
215
  properties?: ConfigField[];
131
216
  /** item type when type === 'array' */
132
217
  items?: ConfigField;
133
- /** credential type slug the field binds to when type === 'credentials-ref' (e.g. 'revenexx:business-central'). */
134
- credentialType?: string;
218
+ /** credential type slug(s) the field binds to when type === 'credentials-ref'
219
+ * (e.g. 'revenexx:business-central'). An array lets a node accept more than
220
+ * one credential type; the picker merges the tenant's credentials across all. */
221
+ credentialType?: string | string[];
222
+ /** keys of other config fields this field's options/schema depend on. The
223
+ * driving `select` fields also form the node's canvas subtitle. */
224
+ dependsOn?: string[];
135
225
  validation?: {
136
226
  pattern?: string;
137
227
  min?: number;
@@ -140,6 +230,20 @@ export interface ConfigField {
140
230
  maxLength?: number;
141
231
  };
142
232
  }
233
+ export interface ResolveOption {
234
+ value: string | number | boolean;
235
+ label?: LocalizedString;
236
+ }
237
+ export interface NodeConfigResolveResult {
238
+ options?: ResolveOption[];
239
+ fields?: ConfigField[];
240
+ outputs?: NodeManifestOutput[];
241
+ }
242
+ export type ResolveErrorKind = 'building' | 'runtime' | 'forbidden' | 'notfound' | 'generic';
243
+ export interface NodeConfigResolveError {
244
+ kind: ResolveErrorKind;
245
+ message: string;
246
+ }
143
247
  export interface NodeManifest {
144
248
  slug: string;
145
249
  version: string;
@@ -148,9 +252,27 @@ export interface NodeManifest {
148
252
  description?: LocalizedString;
149
253
  /** lucide-vue-next icon name OR mdi:icon-id. */
150
254
  icon?: string;
255
+ /**
256
+ * Curated palette group path (e.g. ["Business Central", "Sales Orders"]).
257
+ * Optional manifest field; when absent the palette derives a path from the
258
+ * source package + slug heuristics.
259
+ */
260
+ groups?: LocalizedString[];
261
+ /** Brand images (category `logo` drives palette/canvas rendering); src is a signed URL. */
262
+ images?: Array<{
263
+ src: string;
264
+ alt?: LocalizedString;
265
+ title?: LocalizedString;
266
+ category?: string;
267
+ }>;
151
268
  input: NodeManifestInput;
152
269
  outputs: NodeManifestOutput[];
153
270
  config?: ConfigField[];
271
+ /** Source package (editor-side enrichment from GET /nodes; not manifest data). */
272
+ __package?: {
273
+ name: string;
274
+ label: string | null;
275
+ };
154
276
  }
155
277
  export interface SecretRecord {
156
278
  key: string;
@@ -178,12 +300,21 @@ export interface CredentialTypeField {
178
300
  };
179
301
  }
180
302
  /** A globally-registered credential type — drives the dynamic credential form. */
303
+ /** A brand/logo image shipped with a credential type. `src` is a short-lived
304
+ * signed URL minted by the credential-types API (ManifestImageUrlSigner). */
305
+ export interface CredentialTypeImage {
306
+ src: string;
307
+ alt?: LocalizedString;
308
+ category?: 'logo' | 'icon' | 'banner' | 'screenshot' | 'other' | string;
309
+ }
181
310
  export interface CredentialType {
182
311
  slug: string;
183
312
  version: string;
184
313
  name: LocalizedString;
185
314
  description?: LocalizedString;
186
315
  icon?: string;
316
+ /** Signed logo/brand image URLs from the API (prefer `category: 'logo'`). */
317
+ images?: CredentialTypeImage[];
187
318
  auth_kind: AuthKind;
188
319
  fields: CredentialTypeField[];
189
320
  }
@@ -196,6 +327,10 @@ export interface Credential {
196
327
  status: CredentialStatus;
197
328
  /** non-secret field values only (whitelisted types). */
198
329
  public_config?: Record<string, unknown>;
330
+ /** Outcome of the last connection test (persisted by the API). Null when never tested. */
331
+ last_test_at?: string | null;
332
+ last_test_ok?: boolean | null;
333
+ last_test_message?: string | null;
199
334
  created_at: string;
200
335
  updated_at: string;
201
336
  }
@@ -250,12 +385,30 @@ export interface ScheduleEntry {
250
385
  nextRunAt: string | null;
251
386
  lastRunAt: string | null;
252
387
  }
253
- export type TemplateCategory = 'sync' | 'notification' | 'enrichment' | 'fulfilment' | 'reporting' | 'finance';
388
+ export type TemplateCategory = 'sync' | 'notification' | 'enrichment' | 'fulfilment' | 'reporting' | 'finance' | 'commerce' | 'sales';
254
389
  export interface WorkflowTemplate {
255
390
  /** kebab-case stable id. */
256
391
  slug: string;
257
392
  name: string;
393
+ /** Plain one-liner for cards and pickers (shortDescription, or a de-markdowned excerpt). */
258
394
  description: string;
395
+ /** The full registry description as Markdown; rendered on the detail page. */
396
+ descriptionMd: string;
397
+ /** Trigger types from the catalogue listing (schedule/webhook/manual/event). */
398
+ triggerTypes: TriggerType[];
399
+ /** Registry images (banner/screenshot categories, signed URLs). */
400
+ images: Array<{
401
+ src: string;
402
+ category?: string;
403
+ alt?: unknown;
404
+ }>;
405
+ /** True once the heavy definition blob has been merged via fetchDetail. */
406
+ detailLoaded?: boolean;
407
+ /** Source node package this template ships with (e.g. Business Central). */
408
+ package?: {
409
+ name: string;
410
+ label: string | null;
411
+ } | null;
259
412
  category: TemplateCategory;
260
413
  industries: Array<'mechanical-engineering' | 'chemicals' | 'wholesale' | 'manufacturing' | 'retail' | 'any'>;
261
414
  /** Vendor slugs this template wires together (used to compute prerequisites). */
@@ -325,11 +478,42 @@ export interface WorkflowRunNodeStep {
325
478
  attempt: number;
326
479
  durationMs: number | null;
327
480
  }
481
+ export interface RunStepRow {
482
+ node_id: string;
483
+ node_slug: string;
484
+ activity_type: string;
485
+ status: string;
486
+ attempt: number;
487
+ scheduled_event_id: number | null;
488
+ duration_ms: number | null;
489
+ branch?: string | null;
490
+ error?: string | null;
491
+ input?: unknown;
492
+ output?: unknown;
493
+ /** Resolved node config for this step (present with include_payloads). */
494
+ config?: unknown;
495
+ scheduled_at?: string | null;
496
+ started_at?: string | null;
497
+ completed_at?: string | null;
498
+ }
499
+ export interface DeadLetterRow {
500
+ id: number;
501
+ workflow_id: number;
502
+ trigger_id: string | null;
503
+ source: string | null;
504
+ trigger_handle: string | null;
505
+ reason: string | null;
506
+ status: 'pending' | 'replayed' | 'discarded' | string;
507
+ created_at: string | null;
508
+ updated_at: string | null;
509
+ }
328
510
  export interface WorkflowRun {
329
511
  id: string;
330
512
  workflowId: number;
331
513
  workflowName: string;
332
514
  status: RunStatus;
515
+ /** Temporal run id for this execution; null when the row predates Temporal. */
516
+ temporalRunId?: string | null;
333
517
  triggerId: string;
334
518
  triggerType: TriggerType;
335
519
  startedAt: string;
@@ -348,6 +532,8 @@ export interface OutpostEvent {
348
532
  id: string;
349
533
  /** Topic the event was published on, e.g. "order.created". */
350
534
  topic: string;
535
+ /** Tenant slug from the envelope (empty when the source omits it). */
536
+ tenantId?: string;
351
537
  /** ISO timestamp the event was received. */
352
538
  time: string;
353
539
  /** Whether delivery may be retried (eligible_for_retry). */
@@ -0,0 +1,28 @@
1
+ import type { Credential } from '../types/integrations.js';
2
+ /**
3
+ * The honest, operator-facing state of a credential — derived from the
4
+ * persisted connection-test result, NOT from `status` (which only tracks the
5
+ * OAuth refresh lifecycle and defaults to `active` on create, so it can't say
6
+ * whether the access data actually works).
7
+ *
8
+ * needs_reauth → an OAuth refresh failed; reconnect required
9
+ * connected → the last connection test passed
10
+ * failed → the last connection test failed (see `message`)
11
+ * untested → never tested — deliberately distinct from a passing test
12
+ */
13
+ export type CredentialConnState = 'connected' | 'failed' | 'needs_reauth' | 'untested';
14
+ export interface CredentialStateInfo {
15
+ key: CredentialConnState;
16
+ label: string;
17
+ /** Badge/pill classes for this state. */
18
+ toneClass: string;
19
+ /** Failure detail, when the state is `failed`. */
20
+ message?: string | null;
21
+ /** When the underlying result was recorded (ISO). */
22
+ at?: string | null;
23
+ }
24
+ type TestableCredential = Pick<Credential, 'status' | 'last_test_ok' | 'last_test_at' | 'last_test_message'>;
25
+ export declare function credentialState(c: TestableCredential): CredentialStateInfo;
26
+ /** "just now" / "3m ago" / "2h ago" / "5d ago" / a date. Empty for no timestamp. */
27
+ export declare function relativeTime(iso?: string | null): string;
28
+ export {};
@@ -0,0 +1,29 @@
1
+ const TONE = {
2
+ connected: "bg-emerald-500/10 border-emerald-500/20 text-emerald-700",
3
+ failed: "bg-rose-500/10 border-rose-500/20 text-rose-700",
4
+ needs_reauth: "bg-amber-500/10 border-amber-500/20 text-amber-700",
5
+ untested: "bg-slate-500/10 border-slate-500/20 text-slate-600"
6
+ };
7
+ export function credentialState(c) {
8
+ if (c.status === "needs_reauth") {
9
+ return { key: "needs_reauth", label: "Needs reconnect", toneClass: TONE.needs_reauth, at: c.last_test_at };
10
+ }
11
+ if (c.last_test_ok === true) {
12
+ return { key: "connected", label: "Connected", toneClass: TONE.connected, at: c.last_test_at };
13
+ }
14
+ if (c.last_test_ok === false) {
15
+ return { key: "failed", label: "Failed", toneClass: TONE.failed, message: c.last_test_message, at: c.last_test_at };
16
+ }
17
+ return { key: "untested", label: "Not Connected", toneClass: TONE.untested };
18
+ }
19
+ export function relativeTime(iso) {
20
+ if (!iso) return "";
21
+ const t = new Date(iso).getTime();
22
+ if (Number.isNaN(t)) return "";
23
+ const ms = Date.now() - t;
24
+ if (ms < 6e4) return "just now";
25
+ if (ms < 36e5) return `${Math.floor(ms / 6e4)}m ago`;
26
+ if (ms < 864e5) return `${Math.floor(ms / 36e5)}h ago`;
27
+ if (ms < 30 * 864e5) return `${Math.floor(ms / 864e5)}d ago`;
28
+ return new Date(iso).toLocaleDateString();
29
+ }
@@ -4,6 +4,8 @@
4
4
  * object `{de:"…", en:"…"}` (the HTTP client does JSON decoding automatically).
5
5
  * JSON-encoded strings like `'{"de":"…","en":"…"}'` are also handled.
6
6
  *
7
- * Priority: 1) current browser locale language, 2) "en", 3) first key found.
7
+ * Priority: 1) "en", 2) first key found. English is the cockpit default —
8
+ * there is no working language switcher yet; browser-locale preference
9
+ * returns together with the switcher.
8
10
  */
9
11
  export declare function resolveLocalized(value: unknown): string;
@@ -4,8 +4,7 @@ export function resolveLocalized(value) {
4
4
  const map = value;
5
5
  const keys = Object.keys(map);
6
6
  if (!keys.length) return "";
7
- const lang = typeof navigator !== "undefined" ? navigator.language?.split("-")[0]?.toLowerCase() ?? "en" : "en";
8
- const picked = map[lang] ?? map["en"] ?? map[keys[0]];
7
+ const picked = map["en"] ?? map[keys[0]];
9
8
  return typeof picked === "string" ? picked : "";
10
9
  }
11
10
  if (typeof value !== "string") return String(value);
@@ -13,3 +13,9 @@ export declare function statusTone(status: string | null | undefined): StatusTon
13
13
  export declare function statusClass(status: string | null | undefined): string;
14
14
  /** Tone classes directly (when you already have a tone). */
15
15
  export declare function toneClass(tone: StatusTone): string;
16
+ /**
17
+ * Raw API/Temporal run vocabulary → the cockpit's run-status labels, so a run
18
+ * never reads "completed" in one surface and "succeeded" in another. Unknown
19
+ * statuses pass through lowercased.
20
+ */
21
+ export declare function runStatusLabel(status: string | null | undefined): string;
@@ -76,3 +76,18 @@ export function statusClass(status) {
76
76
  export function toneClass(tone) {
77
77
  return TONE_CLASS[tone];
78
78
  }
79
+ export function runStatusLabel(status) {
80
+ if (!status) return "";
81
+ const s = String(status).toLowerCase().replace(/[\s-]+/g, "_");
82
+ switch (s) {
83
+ case "completed":
84
+ return "succeeded";
85
+ case "pending":
86
+ return "queued";
87
+ case "terminated":
88
+ case "timed_out":
89
+ return "failed";
90
+ default:
91
+ return s;
92
+ }
93
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@revenexx/studio-integrations",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "type": "module",
5
5
  "description": "Integration Studio (core: workflows, node editor, runs, schedules, credentials, secrets, audit) as a standalone, publishable Nuxt module.",
6
6
  "main": "./dist/module.mjs",
@@ -25,14 +25,24 @@
25
25
  "peerDependencies": {
26
26
  "@dagrejs/dagre": "^3.0.0",
27
27
  "@revenexx/studio": "*",
28
+ "@solar-icons/nuxt": "^1.2.0",
29
+ "@tiptap/core": "^3.28.0",
30
+ "@tiptap/extension-document": "^3.28.0",
31
+ "@tiptap/extension-history": "^3.28.0",
32
+ "@tiptap/extension-paragraph": "^3.28.0",
33
+ "@tiptap/extension-text": "^3.28.0",
34
+ "@tiptap/pm": "^3.28.0",
35
+ "@tiptap/vue-3": "^3.28.0",
28
36
  "@vue-flow/background": "^1.3.2",
29
37
  "@vue-flow/controls": "^1.1.3",
30
38
  "@vue-flow/core": "^1.48.2",
31
39
  "@vue-flow/minimap": "^1.5.4",
40
+ "@vue-flow/node-toolbar": "^1.1.1",
32
41
  "lucide-vue-next": "^1.0.0",
33
42
  "nuxt": "^4.0.0",
34
43
  "vue": "^3.5.0",
35
- "@revenexx/studio-shared": "0.1.1"
44
+ "vue-sonner": "^2.0.0",
45
+ "@revenexx/studio-shared": "0.1.2"
36
46
  },
37
47
  "devDependencies": {
38
48
  "@nuxt/module-builder": "^1.0.2",
@@ -1,169 +0,0 @@
1
- <script setup>
2
- import { computed, ref, watch } from "vue";
3
- import { RefreshCircle as Loader2, ArrowRightUp as OpenIcon } from "#solar-icons";
4
- import {
5
- Badge,
6
- Button,
7
- Dialog,
8
- DialogContent,
9
- DialogDescription,
10
- DialogHeader,
11
- DialogTitle
12
- } from "@revenexx/studio";
13
- import { statusClass } from "../../utils/statusStyle";
14
- const props = defineProps({
15
- open: { type: Boolean, required: true },
16
- run: { type: [Object, null], required: true }
17
- });
18
- const emit = defineEmits(["update:open"]);
19
- const router = useRouter();
20
- const { getRun, getHistory, getStackTrace } = useIntegrationsRuns();
21
- const loading = ref(false);
22
- const result = ref(null);
23
- const history = ref([]);
24
- const historySource = ref(null);
25
- const stackTrace = ref(null);
26
- const loadError = ref(null);
27
- function fmt(iso) {
28
- if (!iso) return "\u2014";
29
- try {
30
- return new Date(iso).toLocaleString();
31
- } catch {
32
- return iso;
33
- }
34
- }
35
- function fmtDuration(ms) {
36
- if (ms == null) return "\u2014";
37
- if (ms < 1e3) return `${ms}ms`;
38
- if (ms < 6e4) return `${(ms / 1e3).toFixed(2)}s`;
39
- return `${(ms / 6e4).toFixed(1)}m`;
40
- }
41
- const hasResult = computed(() => result.value != null && Object.keys(result.value).length > 0);
42
- async function load() {
43
- const run = props.run;
44
- if (!run) return;
45
- loading.value = true;
46
- loadError.value = null;
47
- result.value = null;
48
- history.value = [];
49
- stackTrace.value = null;
50
- try {
51
- const [runRes, histRes] = await Promise.allSettled([
52
- getRun(run.workflowId, run.id),
53
- getHistory(run.workflowId, run.id, { page_size: 50 })
54
- ]);
55
- if (runRes.status === "fulfilled") {
56
- result.value = runRes.value?.result ?? null;
57
- }
58
- if (histRes.status === "fulfilled") {
59
- history.value = histRes.value?.data ?? [];
60
- historySource.value = histRes.value?.source ?? null;
61
- }
62
- if (run.status === "running" || run.status === "failed") {
63
- try {
64
- stackTrace.value = await getStackTrace(run.workflowId, run.id);
65
- } catch {
66
- stackTrace.value = null;
67
- }
68
- }
69
- } catch (e) {
70
- loadError.value = e?.message ?? "Could not load run details.";
71
- } finally {
72
- loading.value = false;
73
- }
74
- }
75
- watch(() => [props.open, props.run?.id], () => {
76
- if (props.open && props.run) void load();
77
- }, { immediate: true });
78
- function openWorkflow() {
79
- if (props.run) router.push(`/integrations/workflows/${props.run.workflowId}`);
80
- }
81
- function eventLabel(ev) {
82
- return String(ev.event_type ?? ev.eventType ?? ev.type ?? "event");
83
- }
84
- </script>
85
-
86
- <template>
87
- <Dialog :open="open" @update:open="(v) => emit('update:open', v)">
88
- <DialogContent class="sm:max-w-2xl">
89
- <DialogHeader>
90
- <DialogTitle class="flex items-center gap-2">
91
- <span class="truncate">{{ run?.workflowName ?? "Run" }}</span>
92
- <Badge v-if="run" variant="outline" :class="['text-sm border', statusClass(run.status)]">
93
- {{ run.status }}
94
- </Badge>
95
- </DialogTitle>
96
- <DialogDescription class="font-mono text-sm">
97
- Run {{ run?.id }}
98
- </DialogDescription>
99
- </DialogHeader>
100
-
101
- <div v-if="run" class="space-y-4 max-h-[32rem] overflow-y-auto pr-1">
102
- <!-- Facts -->
103
- <div class="grid grid-cols-2 gap-x-6 gap-y-1 text-sm">
104
- <div class="flex justify-between"><span class="text-muted-foreground">Trigger</span><span>{{ run.triggerType }}</span></div>
105
- <div class="flex justify-between"><span class="text-muted-foreground">Duration</span><span class="tabular-nums">{{ fmtDuration(run.durationMs) }}</span></div>
106
- <div class="flex justify-between"><span class="text-muted-foreground">Started</span><span class="tabular-nums">{{ fmt(run.startedAt) }}</span></div>
107
- <div class="flex justify-between"><span class="text-muted-foreground">Finished</span><span class="tabular-nums">{{ fmt(run.finishedAt) }}</span></div>
108
- </div>
109
-
110
- <div v-if="run.errorMessage" class="rounded-md border border-rose-500/20 bg-rose-500/5 px-3 py-2 text-sm text-rose-700">
111
- {{ run.errorMessage }}
112
- </div>
113
-
114
- <div v-if="loading" class="flex items-center gap-2 text-sm text-muted-foreground">
115
- <Loader2 :size="14" class="animate-spin" /> Loading details…
116
- </div>
117
- <div v-else-if="loadError" class="text-sm text-rose-700">
118
- {{ loadError }}
119
- </div>
120
-
121
- <template v-else>
122
- <!-- Result -->
123
- <section v-if="hasResult" class="space-y-1">
124
- <h4 class="text-mono text-muted-foreground">
125
- Result
126
- </h4>
127
- <pre class="rounded-md bg-muted/40 p-3 text-sm overflow-x-auto max-h-56">{{ JSON.stringify(result, null, 2) }}</pre>
128
- </section>
129
-
130
- <!-- History -->
131
- <section v-if="history.length" class="space-y-1">
132
- <h4 class="text-mono text-muted-foreground">
133
- History <span v-if="historySource">· {{ historySource }}</span>
134
- </h4>
135
- <ol class="space-y-1">
136
- <li
137
- v-for="(ev, i) in history"
138
- :key="i"
139
- class="flex items-center gap-2 rounded-md border border-black/[0.05] bg-white px-2 py-1 text-sm"
140
- >
141
- <span class="tabular-nums text-muted-foreground w-8 shrink-0">{{ ev.event_id ?? i + 1 }}</span>
142
- <span class="font-mono truncate">{{ eventLabel(ev) }}</span>
143
- </li>
144
- </ol>
145
- </section>
146
-
147
- <!-- Stack trace -->
148
- <section v-if="stackTrace" class="space-y-1">
149
- <h4 class="text-mono text-muted-foreground">
150
- Stack trace
151
- </h4>
152
- <pre class="rounded-md bg-slate-900 text-slate-100 p-3 text-sm overflow-x-auto max-h-56">{{ stackTrace }}</pre>
153
- </section>
154
-
155
- <p v-if="!hasResult && !history.length && !stackTrace" class="text-sm text-muted-foreground">
156
- No execution detail available for this run yet.
157
- </p>
158
- </template>
159
- </div>
160
-
161
- <div class="flex justify-end pt-2">
162
- <Button variant="outline" size="sm" @click="openWorkflow">
163
- <OpenIcon :size="14" class="mr-1" />
164
- Open workflow
165
- </Button>
166
- </div>
167
- </DialogContent>
168
- </Dialog>
169
- </template>