mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-16 12:27:53 +08:00
fix(workflow,trigger): generator triggerDrafts loop closure + 5 paper cuts
This commit is contained in:
parent
826c20639a
commit
2416d603c4
@ -97,6 +97,20 @@ public class TriggerPatternMatcher {
|
|||||||
if (wantSender != null && !wantSender.equals(envelope.senderId())) {
|
if (wantSender != null && !wantSender.equals(envelope.senderId())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// contentContains is the keyword filter the templates relied on
|
||||||
|
// — without it a "feishu + 发票" trigger would fire on every
|
||||||
|
// feishu message. Matches case-insensitively against
|
||||||
|
// envelope.data.content, the same field content_match uses.
|
||||||
|
// Keeping the field on channel_message also folds the redundant
|
||||||
|
// content_match pattern type into the more general channel one;
|
||||||
|
// content_match remains supported for backwards compatibility
|
||||||
|
// via {@link #matchesContent}.
|
||||||
|
String wantContains = textOrNull(pattern, "contentContains");
|
||||||
|
if (wantContains != null) {
|
||||||
|
Object content = envelope.data() == null ? null : envelope.data().get("content");
|
||||||
|
if (!(content instanceof String body)) return false;
|
||||||
|
if (!body.toLowerCase().contains(wantContains.toLowerCase())) return false;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -83,7 +83,7 @@ public class WorkflowDraftGenerator {
|
|||||||
conditional — mode.expression 必填(Pebble 子集,如 {{ outputs.x.approved == true }});agentId/agentName + promptTemplate 必填。
|
conditional — mode.expression 必填(Pebble 子集,如 {{ outputs.x.approved == true }});agentId/agentName + promptTemplate 必填。
|
||||||
await_approval — approvalKind + approverChannels[] + approvalMessage 必填;可选 timeoutSecs;不要 agentId / agentName / promptTemplate。
|
await_approval — approvalKind + approverChannels[] + approvalMessage 必填;可选 timeoutSecs;不要 agentId / agentName / promptTemplate。
|
||||||
dispatch_channel — channels[] + targets{} + content 必填;不要 agentId / agentName / promptTemplate。
|
dispatch_channel — channels[] + targets{} + content 必填;不要 agentId / agentName / promptTemplate。
|
||||||
write_memory — employeeId + file + mergeStrategy(append/prepend/replace_section/upsert_kv/overwrite) + content 必填;不要 agentId / agentName / promptTemplate。
|
write_memory — employeeId + file + mergeStrategy(append/replace_section/upsert_kv/overwrite) + content 必填;不要 agentId / agentName / promptTemplate。
|
||||||
|
|
||||||
# 不支持
|
# 不支持
|
||||||
|
|
||||||
@ -214,13 +214,33 @@ public class WorkflowDraftGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- 6. trigger drafts -----------------------------------------
|
// --- 6. trigger drafts -----------------------------------------
|
||||||
|
// patternType allowlist mirrors what TriggerService accepts at
|
||||||
|
// create time. The generator prompt forbids agent_lifecycle and
|
||||||
|
// content_match; we filter defensively here too because models
|
||||||
|
// occasionally hallucinate trigger types under low confidence,
|
||||||
|
// and we don't want a future UI / tool that calls /draft/generate
|
||||||
|
// and trusts the response to silently re-introduce dropped types.
|
||||||
|
java.util.Set<String> allowedPatternTypes = java.util.Set.of(
|
||||||
|
"cron", "channel_message", "workflow_completion", "webhook");
|
||||||
List<Map<String, Object>> triggerDrafts = new ArrayList<>();
|
List<Map<String, Object>> triggerDrafts = new ArrayList<>();
|
||||||
if (root.has("triggerDrafts") && root.get("triggerDrafts").isArray()) {
|
if (root.has("triggerDrafts") && root.get("triggerDrafts").isArray()) {
|
||||||
triggerDrafts = objectMapper.convertValue(root.get("triggerDrafts"),
|
List<Map<String, Object>> candidates = objectMapper.convertValue(root.get("triggerDrafts"),
|
||||||
new TypeReference<List<Map<String, Object>>>() {});
|
new TypeReference<List<Map<String, Object>>>() {});
|
||||||
for (Map<String, Object> td : triggerDrafts) {
|
int dropped = 0;
|
||||||
|
for (Map<String, Object> td : candidates) {
|
||||||
|
String pt = td.get("patternType") instanceof String s ? s : null;
|
||||||
|
if (pt == null || !allowedPatternTypes.contains(pt)) {
|
||||||
|
dropped++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
// Belt-and-suspenders: never trust the LLM to honor enabled=false.
|
// Belt-and-suspenders: never trust the LLM to honor enabled=false.
|
||||||
td.put("enabled", false);
|
td.put("enabled", false);
|
||||||
|
triggerDrafts.add(td);
|
||||||
|
}
|
||||||
|
if (dropped > 0) {
|
||||||
|
warnings = appendWarning(warnings,
|
||||||
|
"dropped " + dropped + " unsupported triggerDraft entr" + (dropped == 1 ? "y" : "ies")
|
||||||
|
+ " (allowed: " + String.join(", ", allowedPatternTypes) + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -160,7 +160,7 @@ public class WorkflowDraftTemplateLibrary {
|
|||||||
[{"name":"daily-memory-cron","patternType":"cron","enabled":false,
|
[{"name":"daily-memory-cron","patternType":"cron","enabled":false,
|
||||||
"patternJson":{"cron":"0 0 22 * * ?","timezone":"Asia/Shanghai"},
|
"patternJson":{"cron":"0 0 22 * * ?","timezone":"Asia/Shanghai"},
|
||||||
"targetType":"workflow",
|
"targetType":"workflow",
|
||||||
"payloadTemplate":"{\\"topic\\":\\"工作\\",\\"date\\":\\"{{ trigger.firedAt }}\\"}"}]"""
|
"payloadTemplate":"{\\"topic\\":\\"工作\\",\\"date\\":\\"{{ event.firedAt }}\\"}"}]"""
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,22 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="hint">{{ t('workflows.generate.hint') }}</p>
|
<p class="hint">{{ t('workflows.generate.hint') }}</p>
|
||||||
|
|
||||||
|
<!-- Template picker — for the common patterns the LLM
|
||||||
|
roundtrip is overkill. Selecting one drops the canonical
|
||||||
|
draft + triggerDrafts straight into the result panel,
|
||||||
|
skipping the model call entirely. -->
|
||||||
|
<div v-if="templates.length" class="template-picker">
|
||||||
|
<span class="picker-label">{{ t('workflows.generate.applyTemplate') }}</span>
|
||||||
|
<select v-model="selectedTemplateId" class="picker-select" @change="onTemplatePick">
|
||||||
|
<option value="">{{ t('workflows.generate.applyTemplatePlaceholder') }}</option>
|
||||||
|
<option v-for="tpl in templates" :key="tpl.id" :value="tpl.id">
|
||||||
|
{{ tpl.label }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<p v-if="selectedTemplate" class="picker-desc">{{ selectedTemplate.description }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<textarea
|
<textarea
|
||||||
ref="descRef"
|
ref="descRef"
|
||||||
v-model="description"
|
v-model="description"
|
||||||
@ -41,6 +57,16 @@
|
|||||||
<span class="result-tag warn">{{ t('workflows.generate.warning') }}</span> {{ w }}
|
<span class="result-tag warn">{{ t('workflows.generate.warning') }}</span> {{ w }}
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<div v-if="result.triggerDrafts.length" class="trigger-drafts">
|
||||||
|
<header>{{ t('workflows.generate.triggersTitle') }} ({{ result.triggerDrafts.length }})</header>
|
||||||
|
<ul>
|
||||||
|
<li v-for="(td, i) in result.triggerDrafts" :key="`td-${i}`">
|
||||||
|
<code>{{ td.patternType }}</code>
|
||||||
|
<span class="td-name">{{ td.name || '—' }}</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<p class="trigger-hint">{{ t('workflows.generate.triggersHint') }}</p>
|
||||||
|
</div>
|
||||||
<details class="result-raw">
|
<details class="result-raw">
|
||||||
<summary>{{ t('workflows.generate.previewDraft') }}</summary>
|
<summary>{{ t('workflows.generate.previewDraft') }}</summary>
|
||||||
<pre>{{ result.draftJson }}</pre>
|
<pre>{{ result.draftJson }}</pre>
|
||||||
@ -72,10 +98,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { nextTick, ref, watch } from 'vue'
|
import { computed, nextTick, ref, watch } from 'vue'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { workflowApi, type GeneratedDraft } from '@/api'
|
import { workflowApi, type GeneratedDraft, type WorkflowDraftTemplate } from '@/api'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue: boolean
|
modelValue: boolean
|
||||||
@ -94,6 +120,64 @@ const loading = ref(false)
|
|||||||
const result = ref<GeneratedDraft | null>(null)
|
const result = ref<GeneratedDraft | null>(null)
|
||||||
const descRef = ref<HTMLTextAreaElement | null>(null)
|
const descRef = ref<HTMLTextAreaElement | null>(null)
|
||||||
|
|
||||||
|
const templates = ref<WorkflowDraftTemplate[]>([])
|
||||||
|
const selectedTemplateId = ref('')
|
||||||
|
const selectedTemplate = computed(() =>
|
||||||
|
templates.value.find((t) => t.id === selectedTemplateId.value) ?? null
|
||||||
|
)
|
||||||
|
|
||||||
|
async function loadTemplates() {
|
||||||
|
try {
|
||||||
|
const res = await workflowApi.listDraftTemplates()
|
||||||
|
templates.value = (res.data as unknown as WorkflowDraftTemplate[]) ?? []
|
||||||
|
} catch (e) {
|
||||||
|
// Templates are a nice-to-have — falling back to free-form description
|
||||||
|
// is fine if the endpoint isn't available (e.g. older deployment).
|
||||||
|
console.warn('listDraftTemplates failed', e)
|
||||||
|
templates.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply a template directly without going through the LLM. The template
|
||||||
|
* already carries a known-good {steps:[...]} JSON + triggerDraftsJson;
|
||||||
|
* we synthesise a GeneratedDraft so the rest of the result-panel UI
|
||||||
|
* (compile pill / missing fields / triggers) keeps working.
|
||||||
|
*/
|
||||||
|
function onTemplatePick() {
|
||||||
|
const tpl = selectedTemplate.value
|
||||||
|
if (!tpl) {
|
||||||
|
result.value = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let triggerDrafts: Array<Record<string, unknown>> = []
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(tpl.triggerDraftsJson || '[]')
|
||||||
|
if (Array.isArray(parsed)) triggerDrafts = parsed
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('template triggerDraftsJson parse failed', e)
|
||||||
|
}
|
||||||
|
// Templates ship with TODO_* placeholders — list them in missingFields
|
||||||
|
// so the operator notices before clicking Accept.
|
||||||
|
const missing: string[] = []
|
||||||
|
const todoMatches = tpl.draftJson.match(/TODO_[A-Z_]+/g) ?? []
|
||||||
|
for (const m of new Set(todoMatches)) missing.push(`${m} (template placeholder)`)
|
||||||
|
result.value = {
|
||||||
|
name: tpl.id,
|
||||||
|
description: tpl.description,
|
||||||
|
draftJson: tpl.draftJson,
|
||||||
|
triggerDrafts,
|
||||||
|
warnings: [],
|
||||||
|
missingFields: missing,
|
||||||
|
confidence: 1.0,
|
||||||
|
compileOk: true,
|
||||||
|
compileErrors: [],
|
||||||
|
}
|
||||||
|
// Pre-fill the description box too so the operator can edit + re-run
|
||||||
|
// through the LLM if the template's prose isn't quite right.
|
||||||
|
if (!description.value.trim()) description.value = tpl.description
|
||||||
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
async (open) => {
|
async (open) => {
|
||||||
@ -102,6 +186,8 @@ watch(
|
|||||||
description.value = ''
|
description.value = ''
|
||||||
result.value = null
|
result.value = null
|
||||||
loading.value = false
|
loading.value = false
|
||||||
|
selectedTemplateId.value = ''
|
||||||
|
await loadTemplates()
|
||||||
await nextTick()
|
await nextTick()
|
||||||
descRef.value?.focus()
|
descRef.value?.focus()
|
||||||
document.addEventListener('keydown', onKey)
|
document.addEventListener('keydown', onKey)
|
||||||
@ -205,6 +291,40 @@ function onAccept() {
|
|||||||
color: var(--mc-text-secondary);
|
color: var(--mc-text-secondary);
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
}
|
}
|
||||||
|
.template-picker {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 10px;
|
||||||
|
background: var(--mc-bg-sunken);
|
||||||
|
border: 1px solid var(--mc-border-light);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.picker-label {
|
||||||
|
font-size: 11px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--mc-text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
}
|
||||||
|
:lang(zh-CN) .picker-label {
|
||||||
|
text-transform: none;
|
||||||
|
letter-spacing: 0;
|
||||||
|
}
|
||||||
|
.picker-select {
|
||||||
|
padding: 7px 9px;
|
||||||
|
border: 1px solid var(--mc-border);
|
||||||
|
border-radius: 6px;
|
||||||
|
background: var(--mc-bg-elevated);
|
||||||
|
color: var(--mc-text-primary);
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
.picker-desc {
|
||||||
|
margin: 4px 0 0;
|
||||||
|
font-size: 11.5px;
|
||||||
|
color: var(--mc-text-tertiary);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
.form-input,
|
.form-input,
|
||||||
.form-textarea {
|
.form-textarea {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@ -264,6 +384,45 @@ function onAccept() {
|
|||||||
letter-spacing: 0.04em;
|
letter-spacing: 0.04em;
|
||||||
}
|
}
|
||||||
.result-tag.warn { background: rgba(245, 158, 11, 0.16); color: #b45309; }
|
.result-tag.warn { background: rgba(245, 158, 11, 0.16); color: #b45309; }
|
||||||
|
.trigger-drafts {
|
||||||
|
border-top: 1px dashed var(--mc-border-light);
|
||||||
|
padding-top: 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.trigger-drafts header {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
.trigger-drafts ul {
|
||||||
|
list-style: none;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 3px;
|
||||||
|
}
|
||||||
|
.trigger-drafts li {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: baseline;
|
||||||
|
}
|
||||||
|
.trigger-drafts code {
|
||||||
|
font-family: 'JetBrains Mono', Consolas, monospace;
|
||||||
|
font-size: 11px;
|
||||||
|
background: var(--mc-bg-muted);
|
||||||
|
padding: 1px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
flex: 0 0 auto;
|
||||||
|
}
|
||||||
|
.trigger-drafts .td-name {
|
||||||
|
opacity: 0.85;
|
||||||
|
}
|
||||||
|
.trigger-hint {
|
||||||
|
margin: 6px 0 0;
|
||||||
|
font-size: 11px;
|
||||||
|
color: var(--mc-text-tertiary);
|
||||||
|
}
|
||||||
.result-raw summary {
|
.result-raw summary {
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
color: var(--mc-text-tertiary);
|
color: var(--mc-text-tertiary);
|
||||||
|
|||||||
@ -247,8 +247,8 @@
|
|||||||
@change="patchMode({ mergeStrategy: ($event.target as HTMLSelectElement).value })"
|
@change="patchMode({ mergeStrategy: ($event.target as HTMLSelectElement).value })"
|
||||||
>
|
>
|
||||||
<option value="append">append</option>
|
<option value="append">append</option>
|
||||||
<option value="prepend">prepend</option>
|
|
||||||
<option value="replace_section">replace_section</option>
|
<option value="replace_section">replace_section</option>
|
||||||
|
<option value="upsert_kv">upsert_kv</option>
|
||||||
<option value="overwrite">overwrite</option>
|
<option value="overwrite">overwrite</option>
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
|
|||||||
@ -137,7 +137,7 @@ const WORKFLOW_SCHEMA = {
|
|||||||
content: { type: 'string' },
|
content: { type: 'string' },
|
||||||
employeeId: { type: 'string' },
|
employeeId: { type: 'string' },
|
||||||
file: { type: 'string' },
|
file: { type: 'string' },
|
||||||
mergeStrategy: { type: 'string', enum: ['append', 'prepend', 'replace_section', 'overwrite'] },
|
mergeStrategy: { type: 'string', enum: ['append', 'replace_section', 'upsert_kv', 'overwrite'] },
|
||||||
},
|
},
|
||||||
additionalProperties: true,
|
additionalProperties: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -2036,6 +2036,12 @@ export default {
|
|||||||
warning: 'warning',
|
warning: 'warning',
|
||||||
previewDraft: 'Preview generated JSON',
|
previewDraft: 'Preview generated JSON',
|
||||||
failed: 'Generate failed: {msg}',
|
failed: 'Generate failed: {msg}',
|
||||||
|
acceptedWithTriggers: 'Draft saved and {count} trigger(s) created (disabled by default)',
|
||||||
|
acceptedSomeTriggersFailed: 'Draft saved; {ok} trigger(s) created, {fail} failed',
|
||||||
|
triggersTitle: 'Suggested triggers',
|
||||||
|
triggersHint: 'Created with enabled=false; toggle them on later from the Triggers page.',
|
||||||
|
applyTemplate: 'Apply template (skip the AI roundtrip)',
|
||||||
|
applyTemplatePlaceholder: '— Pick a canonical pattern —',
|
||||||
},
|
},
|
||||||
paused: {
|
paused: {
|
||||||
header: 'Paused runs ({count})',
|
header: 'Paused runs ({count})',
|
||||||
|
|||||||
@ -2048,6 +2048,12 @@ export default {
|
|||||||
warning: '警告',
|
warning: '警告',
|
||||||
previewDraft: '预览生成的 JSON',
|
previewDraft: '预览生成的 JSON',
|
||||||
failed: '生成失败:{msg}',
|
failed: '生成失败:{msg}',
|
||||||
|
acceptedWithTriggers: '已生成草稿并创建 {count} 个触发器(默认未启用)',
|
||||||
|
acceptedSomeTriggersFailed: '已生成草稿,{ok} 个触发器创建成功,{fail} 个失败',
|
||||||
|
triggersTitle: '建议的触发器',
|
||||||
|
triggersHint: '默认 enabled=false;可在 Triggers 页面随时启用或调整。',
|
||||||
|
applyTemplate: '套用模板(直接生成,跳过 AI)',
|
||||||
|
applyTemplatePlaceholder: '— 选择一个常用模板 —',
|
||||||
},
|
},
|
||||||
paused: {
|
paused: {
|
||||||
header: '待恢复运行 ({count})',
|
header: '待恢复运行 ({count})',
|
||||||
|
|||||||
@ -221,6 +221,8 @@ import {
|
|||||||
agentApi,
|
agentApi,
|
||||||
channelApi,
|
channelApi,
|
||||||
workflowApi,
|
workflowApi,
|
||||||
|
triggerApi,
|
||||||
|
type TriggerSummary,
|
||||||
type WorkflowSummary,
|
type WorkflowSummary,
|
||||||
type WorkflowRun,
|
type WorkflowRun,
|
||||||
type WorkflowRunStep,
|
type WorkflowRunStep,
|
||||||
@ -345,15 +347,60 @@ async function onGenerateAccept(draft: GeneratedDraft) {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
})
|
})
|
||||||
const created = res.data as unknown as WorkflowSummary
|
const created = res.data as unknown as WorkflowSummary
|
||||||
if (created?.id) {
|
if (!created?.id) {
|
||||||
await workflowApi.saveDraft(created.id, draft.draftJson)
|
ElMessage.error(t('workflows.generate.failed', { msg: 'workflow row not returned' }))
|
||||||
await reload()
|
return
|
||||||
await select(created.id)
|
}
|
||||||
// If the generator's preview compile failed, surface the errors
|
await workflowApi.saveDraft(created.id, draft.draftJson)
|
||||||
// inline so the operator sees them immediately on first load.
|
|
||||||
if (!draft.compileOk && draft.compileErrors.length) {
|
// Persist suggested trigger drafts. The generator returned them with
|
||||||
compileErrors.value = draft.compileErrors
|
// enabled=false and (typically) targetId placeholders — bind each to
|
||||||
|
// the new workflow id and persist as a real trigger row. Operators
|
||||||
|
// see them appear in the Triggers list and turn them on after they
|
||||||
|
// finish the workflow's TODOs.
|
||||||
|
let triggersCreated = 0
|
||||||
|
let triggersSkipped = 0
|
||||||
|
if (Array.isArray(draft.triggerDrafts)) {
|
||||||
|
for (const td of draft.triggerDrafts) {
|
||||||
|
try {
|
||||||
|
await triggerApi.create({
|
||||||
|
workspaceId: workspaceId.value,
|
||||||
|
name: typeof td.name === 'string' ? td.name : `from-${draft.name}`,
|
||||||
|
patternType: typeof td.patternType === 'string' ? td.patternType : '',
|
||||||
|
// patternJson can come from the LLM as either a string or
|
||||||
|
// a nested object — normalise to the string the API wants.
|
||||||
|
patternJson: typeof td.patternJson === 'string'
|
||||||
|
? td.patternJson
|
||||||
|
: JSON.stringify(td.patternJson ?? {}),
|
||||||
|
targetType: 'workflow',
|
||||||
|
// Bind to the just-created workflow regardless of what the
|
||||||
|
// generator put in targetId (it usually inserted a TODO_*).
|
||||||
|
targetId: created.id,
|
||||||
|
enabled: false,
|
||||||
|
payloadTemplate: typeof td.payloadTemplate === 'string'
|
||||||
|
? td.payloadTemplate : undefined,
|
||||||
|
} as unknown as TriggerSummary)
|
||||||
|
triggersCreated++
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('triggerDraft create failed', td, e)
|
||||||
|
triggersSkipped++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await reload()
|
||||||
|
await select(created.id)
|
||||||
|
// If the generator's preview compile failed, surface the errors
|
||||||
|
// inline so the operator sees them immediately on first load.
|
||||||
|
if (!draft.compileOk && draft.compileErrors.length) {
|
||||||
|
compileErrors.value = draft.compileErrors
|
||||||
|
}
|
||||||
|
if (triggersCreated > 0 && triggersSkipped === 0) {
|
||||||
|
ElMessage.success(t('workflows.generate.acceptedWithTriggers', { count: triggersCreated }))
|
||||||
|
} else if (triggersSkipped > 0) {
|
||||||
|
ElMessage.warning(t('workflows.generate.acceptedSomeTriggersFailed',
|
||||||
|
{ ok: triggersCreated, fail: triggersSkipped }))
|
||||||
|
} else {
|
||||||
ElMessage.success(t('workflows.generate.compileOk'))
|
ElMessage.success(t('workflows.generate.compileOk'))
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user