@nuxtagentic/automations 0.0.0-stage → 0.1.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.
- package/LICENSE.md +9 -0
- package/README.md +173 -2
- package/client.ts +24 -0
- package/module.ts +55 -0
- package/package.json +59 -3
- package/runtime/components/AutomationCanvas.vue +488 -0
- package/runtime/components/AutomationInspector.vue +436 -0
- package/runtime/components/AutomationNodeIcon.vue +27 -0
- package/runtime/components/AutomationRunsPanel.vue +475 -0
- package/runtime/components/AutomationSidebar.vue +253 -0
- package/runtime/components/AutomationStatusIcon.vue +44 -0
- package/runtime/pages/automations.vue +1181 -0
- package/runtime/server/api/automations/[id]/endpoint.get.ts +12 -0
- package/runtime/server/api/automations/[id]/run.post.ts +30 -0
- package/runtime/server/api/automations/events.post.ts +37 -0
- package/runtime/server/api/automations/runs/[id]/resume.post.ts +23 -0
- package/runtime/server/handlers/automation-hook.post.ts +51 -0
- package/runtime/server/handlers/connect-hook.post.ts +54 -0
- package/runtime/server/handlers/integration-hook.post.ts +86 -0
- package/runtime/server/plugins/events.ts +79 -0
- package/runtime/server/plugins/maintenance.ts +20 -0
- package/runtime/server/utils/agent-runner.ts +15 -0
- package/runtime/server/utils/automation-context.ts +77 -0
- package/runtime/server/utils/automation-engine.ts +697 -0
- package/runtime/server/utils/automation-launch.ts +205 -0
- package/runtime/server/utils/automation-schedule.ts +31 -0
- package/runtime/server/utils/connection-events.ts +114 -0
- package/runtime/server/utils/maintenance.ts +255 -0
- package/runtime/server/utils/schedule-sync.ts +32 -0
- package/runtime/server/utils/transient-retry.ts +34 -0
- package/runtime/server/workflows/runner.ts +39 -0
- package/runtime/shared/agent.ts +5 -0
- package/runtime/shared/automation-run.ts +61 -0
- package/runtime/shared/automation.ts +202 -0
- package/runtime/workspace.ts +56 -0
- package/schema.ts +222 -0
- package/server.ts +2 -0
- package/workflow.ts +2 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed } from 'vue'
|
|
3
|
+
import type { AgentSummary } from '@nuxtagentic/agents/runtime/shared/agent-summary'
|
|
4
|
+
import {
|
|
5
|
+
SUCCESS_ACTIONS,
|
|
6
|
+
ERROR_ACTIONS,
|
|
7
|
+
getTargetLabel,
|
|
8
|
+
type AutomationDraft,
|
|
9
|
+
type AutomationNode,
|
|
10
|
+
} from '../shared/automation'
|
|
11
|
+
import AutomationNodeIcon from './AutomationNodeIcon.vue'
|
|
12
|
+
|
|
13
|
+
const draft = defineModel<AutomationDraft>({ required: true })
|
|
14
|
+
const {
|
|
15
|
+
node,
|
|
16
|
+
agents,
|
|
17
|
+
events,
|
|
18
|
+
connectedProviders,
|
|
19
|
+
agentsInstalled = true,
|
|
20
|
+
agentError = '',
|
|
21
|
+
endpoint = '',
|
|
22
|
+
sample = '',
|
|
23
|
+
copied = false,
|
|
24
|
+
} = defineProps<{
|
|
25
|
+
node: AutomationNode
|
|
26
|
+
agents: AgentSummary[]
|
|
27
|
+
events: { id: string; label: string; detail: string }[]
|
|
28
|
+
connectedProviders: string[]
|
|
29
|
+
agentsInstalled?: boolean
|
|
30
|
+
agentError?: string
|
|
31
|
+
endpoint?: string
|
|
32
|
+
sample?: string
|
|
33
|
+
copied?: boolean
|
|
34
|
+
}>()
|
|
35
|
+
defineEmits<{ close: []; copy: [] }>()
|
|
36
|
+
|
|
37
|
+
const isConnected = (provider: string) => connectedProviders.includes(provider)
|
|
38
|
+
const options = computed(() => {
|
|
39
|
+
switch (node) {
|
|
40
|
+
case 'trigger':
|
|
41
|
+
return events.map((event) => ({
|
|
42
|
+
value: event.id,
|
|
43
|
+
label: event.id,
|
|
44
|
+
detail: event.label,
|
|
45
|
+
}))
|
|
46
|
+
case 'agent':
|
|
47
|
+
return agents.map((agent) => ({
|
|
48
|
+
value: agent.id,
|
|
49
|
+
label: agent.name,
|
|
50
|
+
detail: `${agent.provider} · ${agent.model}${agent.status === 'draft' ? ' · draft, test only' : ''}`,
|
|
51
|
+
}))
|
|
52
|
+
case 'success':
|
|
53
|
+
return SUCCESS_ACTIONS
|
|
54
|
+
default:
|
|
55
|
+
return ERROR_ACTIONS
|
|
56
|
+
}
|
|
57
|
+
})
|
|
58
|
+
const outcome = computed(() => (node === 'success' ? draft.value.success : draft.value.failure))
|
|
59
|
+
const panel = computed(
|
|
60
|
+
() =>
|
|
61
|
+
({
|
|
62
|
+
trigger: { label: 'Trigger', color: 'text-warning', optionLabel: 'Event' },
|
|
63
|
+
agent: { label: 'Agent call', color: 'text-info', optionLabel: 'Agent' },
|
|
64
|
+
success: { label: 'On success', color: 'text-success', optionLabel: 'Action' },
|
|
65
|
+
error: { label: 'On error', color: 'text-error', optionLabel: 'Then' },
|
|
66
|
+
})[node],
|
|
67
|
+
)
|
|
68
|
+
const targetPlaceholder = computed(() => {
|
|
69
|
+
switch (outcome.value.action) {
|
|
70
|
+
case 'record':
|
|
71
|
+
return 'description'
|
|
72
|
+
case 'slack':
|
|
73
|
+
return 'C0123456789'
|
|
74
|
+
case 'webhook':
|
|
75
|
+
return 'https://example.com/agent-result'
|
|
76
|
+
default:
|
|
77
|
+
return 'user.email'
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
function pick(value: string | number) {
|
|
82
|
+
if (node === 'trigger') {
|
|
83
|
+
draft.value.trigger.event = String(value)
|
|
84
|
+
} else if (node === 'agent') {
|
|
85
|
+
draft.value.agentId = Number(value)
|
|
86
|
+
} else {
|
|
87
|
+
const action = [...SUCCESS_ACTIONS, ...ERROR_ACTIONS].find((option) => option.value === value)
|
|
88
|
+
if (action) {
|
|
89
|
+
outcome.value.action = action.value
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function isPicked(value: string | number) {
|
|
94
|
+
switch (node) {
|
|
95
|
+
case 'trigger':
|
|
96
|
+
return draft.value.trigger.event === value
|
|
97
|
+
case 'agent':
|
|
98
|
+
return draft.value.agentId === value
|
|
99
|
+
default:
|
|
100
|
+
return outcome.value.action === value
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<template>
|
|
106
|
+
<div class="flex h-full min-h-0 flex-col">
|
|
107
|
+
<div class="flex h-13 shrink-0 items-center gap-2.5 border-b border-(--gray-400) pr-2 pl-4">
|
|
108
|
+
<span
|
|
109
|
+
class="grid size-6 place-items-center rounded-[7px] bg-(--gray-500)"
|
|
110
|
+
:class="panel.color"
|
|
111
|
+
>
|
|
112
|
+
<AutomationNodeIcon
|
|
113
|
+
:node="node"
|
|
114
|
+
class="size-3.5"
|
|
115
|
+
/>
|
|
116
|
+
</span>
|
|
117
|
+
<span class="flex-1 text-[15px]">{{ panel.label }}</span>
|
|
118
|
+
<UButton
|
|
119
|
+
icon="i-lucide-x"
|
|
120
|
+
aria-label="Close inspector"
|
|
121
|
+
color="neutral"
|
|
122
|
+
variant="ghost"
|
|
123
|
+
class="size-8 justify-center rounded-md p-0 text-muted hover:bg-(--gray-400) hover:text-highlighted"
|
|
124
|
+
:ui="{ leadingIcon: 'size-4' }"
|
|
125
|
+
@click="$emit('close')"
|
|
126
|
+
/>
|
|
127
|
+
</div>
|
|
128
|
+
<div class="flex min-h-0 flex-1 flex-col gap-5 overflow-x-hidden overflow-y-auto p-4">
|
|
129
|
+
<div
|
|
130
|
+
v-if="node === 'trigger'"
|
|
131
|
+
class="flex flex-col gap-2"
|
|
132
|
+
>
|
|
133
|
+
<span class="text-xs text-dimmed">Fires on</span>
|
|
134
|
+
<UTabs
|
|
135
|
+
v-model="draft.trigger.kind"
|
|
136
|
+
:items="[
|
|
137
|
+
{ label: 'Event', value: 'event' },
|
|
138
|
+
{ label: 'Webhook', value: 'webhook' },
|
|
139
|
+
{ label: 'Schedule', value: 'schedule' },
|
|
140
|
+
]"
|
|
141
|
+
:content="false"
|
|
142
|
+
color="neutral"
|
|
143
|
+
:ui="{
|
|
144
|
+
list: 'h-[34px] rounded-[9px] border border-(--gray-400) bg-(--gray-100) p-0.5',
|
|
145
|
+
trigger: 'text-[13px] font-normal data-[state=active]:text-highlighted',
|
|
146
|
+
indicator: 'rounded-[7px] bg-(--gray-500) ring-0',
|
|
147
|
+
}"
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
<template v-if="node === 'trigger' && draft.trigger.kind === 'webhook'">
|
|
151
|
+
<div class="flex flex-col gap-2">
|
|
152
|
+
<span class="text-xs text-dimmed">Endpoint</span>
|
|
153
|
+
<div
|
|
154
|
+
class="flex h-9 items-center gap-1.5 rounded-lg border border-(--gray-400) bg-(--gray-100) pr-1 pl-2.5"
|
|
155
|
+
>
|
|
156
|
+
<span class="font-mono text-[11px] text-warning">POST</span>
|
|
157
|
+
<span class="min-w-0 flex-1 truncate font-mono text-xs">
|
|
158
|
+
{{ endpoint || 'Save to generate endpoint' }}
|
|
159
|
+
</span>
|
|
160
|
+
<UButton
|
|
161
|
+
:label="copied ? 'Copied' : 'Copy'"
|
|
162
|
+
color="neutral"
|
|
163
|
+
variant="soft"
|
|
164
|
+
size="xs"
|
|
165
|
+
class="h-7 rounded-md"
|
|
166
|
+
:disabled="!endpoint"
|
|
167
|
+
@click="$emit('copy')"
|
|
168
|
+
/>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
<UFormField
|
|
172
|
+
label="Path"
|
|
173
|
+
:ui="{ label: 'text-xs font-normal text-dimmed' }"
|
|
174
|
+
>
|
|
175
|
+
<UInput
|
|
176
|
+
v-model="draft.trigger.slug"
|
|
177
|
+
class="w-full"
|
|
178
|
+
:ui="{
|
|
179
|
+
base: 'h-9 rounded-lg bg-(--gray-100) px-2.5 font-mono text-[13px] ring-(--gray-400)',
|
|
180
|
+
}"
|
|
181
|
+
/>
|
|
182
|
+
</UFormField>
|
|
183
|
+
<div class="flex flex-col gap-2">
|
|
184
|
+
<span class="text-xs text-dimmed">Sample payload</span>
|
|
185
|
+
<pre
|
|
186
|
+
class="rounded-lg border border-(--gray-400) bg-(--gray-100) p-3 font-mono text-xs leading-[18px] whitespace-pre-wrap text-toned"
|
|
187
|
+
>{{ sample }}</pre>
|
|
188
|
+
<span class="text-xs leading-4 text-dimmed">
|
|
189
|
+
Send a unique Idempotency-Key header with each event.
|
|
190
|
+
</span>
|
|
191
|
+
</div>
|
|
192
|
+
</template>
|
|
193
|
+
<div
|
|
194
|
+
v-else-if="node === 'trigger' && draft.trigger.kind === 'schedule'"
|
|
195
|
+
class="flex flex-col gap-3"
|
|
196
|
+
>
|
|
197
|
+
<UFormField
|
|
198
|
+
label="Cron expression"
|
|
199
|
+
help="Five fields: minute, hour, day, month, weekday."
|
|
200
|
+
>
|
|
201
|
+
<UInput
|
|
202
|
+
v-model="draft.trigger.cron"
|
|
203
|
+
placeholder="0 9 * * 1-5"
|
|
204
|
+
class="w-full"
|
|
205
|
+
/>
|
|
206
|
+
</UFormField>
|
|
207
|
+
<UFormField
|
|
208
|
+
label="Time zone"
|
|
209
|
+
help="Schedules follow this zone, including daylight saving time."
|
|
210
|
+
>
|
|
211
|
+
<UInput
|
|
212
|
+
v-model="draft.trigger.timeZone"
|
|
213
|
+
placeholder="Europe/Amsterdam"
|
|
214
|
+
class="w-full"
|
|
215
|
+
/>
|
|
216
|
+
</UFormField>
|
|
217
|
+
<p class="text-xs text-muted">
|
|
218
|
+
Activate and save to schedule the next run. Pausing cancels future runs.
|
|
219
|
+
</p>
|
|
220
|
+
</div>
|
|
221
|
+
<div
|
|
222
|
+
v-else
|
|
223
|
+
class="flex flex-col gap-2"
|
|
224
|
+
>
|
|
225
|
+
<span class="text-xs text-dimmed">
|
|
226
|
+
{{ panel.optionLabel }}
|
|
227
|
+
</span>
|
|
228
|
+
<div class="flex flex-col gap-0.5">
|
|
229
|
+
<button
|
|
230
|
+
v-for="option in options"
|
|
231
|
+
:key="option.value"
|
|
232
|
+
type="button"
|
|
233
|
+
class="flex min-h-12 cursor-pointer items-center gap-2.5 rounded-lg px-2.5 py-1.5 text-left transition-colors focus-visible:outline-2 focus-visible:outline-primary motion-reduce:transition-none"
|
|
234
|
+
:class="isPicked(option.value) ? 'bg-(--gray-400)' : 'hover:bg-(--gray-400)'"
|
|
235
|
+
:aria-pressed="isPicked(option.value)"
|
|
236
|
+
@click="pick(option.value)"
|
|
237
|
+
>
|
|
238
|
+
<span class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
239
|
+
<span
|
|
240
|
+
class="text-[13px]"
|
|
241
|
+
:class="[node === 'trigger' && 'font-mono', 'text-highlighted']"
|
|
242
|
+
>
|
|
243
|
+
{{ option.label }}
|
|
244
|
+
</span>
|
|
245
|
+
<span class="text-xs text-muted">{{ option.detail }}</span>
|
|
246
|
+
</span>
|
|
247
|
+
<svg
|
|
248
|
+
viewBox="0 0 24 24"
|
|
249
|
+
fill="none"
|
|
250
|
+
stroke="currentColor"
|
|
251
|
+
stroke-width="2"
|
|
252
|
+
stroke-linecap="round"
|
|
253
|
+
stroke-linejoin="round"
|
|
254
|
+
aria-hidden="true"
|
|
255
|
+
class="size-4 shrink-0 text-highlighted transition-[opacity,transform] duration-200 motion-reduce:transition-none"
|
|
256
|
+
:class="isPicked(option.value) ? 'scale-100 opacity-100' : 'scale-80 opacity-0'"
|
|
257
|
+
>
|
|
258
|
+
<path d="M20 6 9 17l-5-5" />
|
|
259
|
+
</svg>
|
|
260
|
+
</button>
|
|
261
|
+
</div>
|
|
262
|
+
<UButton
|
|
263
|
+
v-if="node === 'agent' && (!agents.length || !agentsInstalled)"
|
|
264
|
+
:to="agentsInstalled ? '/dashboard/agents' : '/dashboard/modules?search=Agents'"
|
|
265
|
+
:label="agentsInstalled ? 'Create an agent' : 'Install Agents'"
|
|
266
|
+
color="neutral"
|
|
267
|
+
variant="outline"
|
|
268
|
+
icon="i-lucide-bot"
|
|
269
|
+
/>
|
|
270
|
+
<p
|
|
271
|
+
v-if="node === 'agent' && agentError"
|
|
272
|
+
class="text-xs text-error"
|
|
273
|
+
>
|
|
274
|
+
{{ agentError }}
|
|
275
|
+
</p>
|
|
276
|
+
</div>
|
|
277
|
+
<template v-if="node === 'agent'">
|
|
278
|
+
<UFormField
|
|
279
|
+
label="Instructions"
|
|
280
|
+
:ui="{ label: 'text-xs font-normal text-dimmed' }"
|
|
281
|
+
>
|
|
282
|
+
<UTextarea
|
|
283
|
+
v-model="draft.instructions"
|
|
284
|
+
:rows="5"
|
|
285
|
+
class="w-full"
|
|
286
|
+
:ui="{
|
|
287
|
+
base: 'rounded-lg bg-(--gray-100) p-2.5 text-[13px] leading-5 ring-(--gray-400)',
|
|
288
|
+
}"
|
|
289
|
+
/>
|
|
290
|
+
</UFormField>
|
|
291
|
+
<div class="flex flex-col gap-2">
|
|
292
|
+
<span class="text-xs text-dimmed">Timeout</span>
|
|
293
|
+
<div class="flex gap-1.5">
|
|
294
|
+
<button
|
|
295
|
+
v-for="timeout in [15, 30, 60]"
|
|
296
|
+
:key="timeout"
|
|
297
|
+
type="button"
|
|
298
|
+
class="h-[30px] rounded-full border px-3 text-xs transition-colors duration-140 focus-visible:outline-2 focus-visible:outline-primary motion-reduce:transition-none"
|
|
299
|
+
:class="
|
|
300
|
+
draft.timeout === timeout
|
|
301
|
+
? 'border-(--gray-600) bg-(--gray-500) text-highlighted'
|
|
302
|
+
: 'border-(--gray-500) text-muted hover:bg-(--gray-400)'
|
|
303
|
+
"
|
|
304
|
+
:aria-pressed="draft.timeout === timeout"
|
|
305
|
+
@click="draft.timeout = timeout"
|
|
306
|
+
>
|
|
307
|
+
{{ timeout }}s
|
|
308
|
+
</button>
|
|
309
|
+
</div>
|
|
310
|
+
</div>
|
|
311
|
+
<div class="flex flex-col gap-2">
|
|
312
|
+
<span class="text-xs text-dimmed">Acts as</span>
|
|
313
|
+
<div class="flex gap-1.5">
|
|
314
|
+
<button
|
|
315
|
+
v-for="option in [
|
|
316
|
+
{ value: 'owner', label: 'Me' },
|
|
317
|
+
{ value: 'workspace', label: 'Workspace only' },
|
|
318
|
+
] as const"
|
|
319
|
+
:key="option.value"
|
|
320
|
+
type="button"
|
|
321
|
+
class="h-[30px] rounded-full border px-3 text-xs transition-colors duration-140 focus-visible:outline-2 focus-visible:outline-primary motion-reduce:transition-none"
|
|
322
|
+
:class="
|
|
323
|
+
(draft.runAs ?? 'owner') === option.value
|
|
324
|
+
? 'border-(--gray-600) bg-(--gray-500) text-highlighted'
|
|
325
|
+
: 'border-(--gray-500) text-muted hover:bg-(--gray-400)'
|
|
326
|
+
"
|
|
327
|
+
:aria-pressed="(draft.runAs ?? 'owner') === option.value"
|
|
328
|
+
@click="draft.runAs = option.value"
|
|
329
|
+
>
|
|
330
|
+
{{ option.label }}
|
|
331
|
+
</button>
|
|
332
|
+
</div>
|
|
333
|
+
<span class="text-xs leading-4 text-dimmed">
|
|
334
|
+
{{
|
|
335
|
+
draft.runAs === 'workspace'
|
|
336
|
+
? 'Uses only the workspace’s shared connections, never your own accounts.'
|
|
337
|
+
: 'Uses your connections and the workspace’s shared ones.'
|
|
338
|
+
}}
|
|
339
|
+
</span>
|
|
340
|
+
</div>
|
|
341
|
+
</template>
|
|
342
|
+
<div
|
|
343
|
+
v-if="node === 'error'"
|
|
344
|
+
class="flex items-center gap-3"
|
|
345
|
+
>
|
|
346
|
+
<span class="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
347
|
+
<span class="text-[13px] text-highlighted">Retries</span>
|
|
348
|
+
<span class="text-xs text-muted">Call the agent again before handling</span>
|
|
349
|
+
</span>
|
|
350
|
+
<div
|
|
351
|
+
class="flex h-8 shrink-0 items-center rounded-lg border border-(--gray-400) bg-(--gray-100)"
|
|
352
|
+
>
|
|
353
|
+
<UButton
|
|
354
|
+
icon="i-lucide-minus"
|
|
355
|
+
aria-label="Fewer retries"
|
|
356
|
+
color="neutral"
|
|
357
|
+
variant="ghost"
|
|
358
|
+
class="size-7.5 justify-center rounded-[7px] p-0 text-muted"
|
|
359
|
+
:ui="{ leadingIcon: 'size-3.5' }"
|
|
360
|
+
:disabled="draft.retries === 0"
|
|
361
|
+
@click="draft.retries--"
|
|
362
|
+
/>
|
|
363
|
+
<span class="min-w-5 text-center text-[13px] tabular-nums text-highlighted">
|
|
364
|
+
{{ draft.retries }}
|
|
365
|
+
</span>
|
|
366
|
+
<UButton
|
|
367
|
+
icon="i-lucide-plus"
|
|
368
|
+
aria-label="More retries"
|
|
369
|
+
color="neutral"
|
|
370
|
+
variant="ghost"
|
|
371
|
+
class="size-7.5 justify-center rounded-[7px] p-0 text-muted"
|
|
372
|
+
:ui="{ leadingIcon: 'size-3.5' }"
|
|
373
|
+
:disabled="draft.retries === 5"
|
|
374
|
+
@click="draft.retries++"
|
|
375
|
+
/>
|
|
376
|
+
</div>
|
|
377
|
+
</div>
|
|
378
|
+
<template v-if="(node === 'success' || node === 'error') && outcome.action !== 'none'">
|
|
379
|
+
<UFormField
|
|
380
|
+
:label="getTargetLabel(outcome.action)"
|
|
381
|
+
:ui="{ label: 'text-xs font-normal text-dimmed' }"
|
|
382
|
+
>
|
|
383
|
+
<UInput
|
|
384
|
+
v-model="outcome.target"
|
|
385
|
+
:placeholder="targetPlaceholder"
|
|
386
|
+
class="w-full"
|
|
387
|
+
:ui="{
|
|
388
|
+
base: 'h-9 rounded-lg bg-(--gray-100) px-2.5 font-mono text-[13px] ring-(--gray-400)',
|
|
389
|
+
}"
|
|
390
|
+
/>
|
|
391
|
+
</UFormField>
|
|
392
|
+
<p
|
|
393
|
+
v-if="
|
|
394
|
+
(outcome.action === 'email' || outcome.action === 'notify') && !isConnected('gmail')
|
|
395
|
+
"
|
|
396
|
+
class="text-xs leading-4 text-muted"
|
|
397
|
+
>
|
|
398
|
+
Connect Gmail and allow Send email in
|
|
399
|
+
<NuxtLink
|
|
400
|
+
to="/dashboard/integrations?integration=gmail"
|
|
401
|
+
class="underline"
|
|
402
|
+
>
|
|
403
|
+
Integrations
|
|
404
|
+
</NuxtLink>
|
|
405
|
+
.
|
|
406
|
+
</p>
|
|
407
|
+
<p
|
|
408
|
+
v-if="outcome.action === 'slack' && !isConnected('slack')"
|
|
409
|
+
class="text-xs leading-4 text-muted"
|
|
410
|
+
>
|
|
411
|
+
Connect Slack and allow Post messages in
|
|
412
|
+
<NuxtLink
|
|
413
|
+
to="/dashboard/integrations?integration=slack"
|
|
414
|
+
class="underline"
|
|
415
|
+
>
|
|
416
|
+
Integrations
|
|
417
|
+
</NuxtLink>
|
|
418
|
+
.
|
|
419
|
+
</p>
|
|
420
|
+
<p
|
|
421
|
+
v-if="outcome.action === 'record'"
|
|
422
|
+
class="text-xs leading-4 text-muted"
|
|
423
|
+
>
|
|
424
|
+
Enter a writable field on the triggering record. Webhook payloads need record: { model, id
|
|
425
|
+
}.
|
|
426
|
+
</p>
|
|
427
|
+
<p
|
|
428
|
+
v-if="outcome.action === 'webhook'"
|
|
429
|
+
class="text-xs leading-4 text-muted"
|
|
430
|
+
>
|
|
431
|
+
Add the destination’s HTTPS origin to NUXT_INTEGRATIONS_WEBHOOK_ORIGINS on the server.
|
|
432
|
+
</p>
|
|
433
|
+
</template>
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
</template>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import type { AutomationNode } from '../shared/automation'
|
|
3
|
+
|
|
4
|
+
const { node } = defineProps<{ node: AutomationNode }>()
|
|
5
|
+
const paths: Record<AutomationNode, string> = {
|
|
6
|
+
trigger: 'M13 2 3 14h9l-1 8 10-12h-9l1-8Z',
|
|
7
|
+
agent:
|
|
8
|
+
'M12 8V4H8M6 8h12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2ZM2 14h2M20 14h2M15 13v2M9 13v2',
|
|
9
|
+
success: 'M20 6 9 17l-5-5',
|
|
10
|
+
error:
|
|
11
|
+
'M12 9v4M12 17h.01M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0Z',
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<template>
|
|
16
|
+
<svg
|
|
17
|
+
viewBox="0 0 24 24"
|
|
18
|
+
fill="none"
|
|
19
|
+
stroke="currentColor"
|
|
20
|
+
stroke-width="1.75"
|
|
21
|
+
stroke-linecap="round"
|
|
22
|
+
stroke-linejoin="round"
|
|
23
|
+
aria-hidden="true"
|
|
24
|
+
>
|
|
25
|
+
<path :d="paths[node]" />
|
|
26
|
+
</svg>
|
|
27
|
+
</template>
|