@hexabot-ai/api 3.2.2-alpha.10 → 3.2.2-alpha.12
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/dist/actions/actions.service.d.ts +1 -0
- package/dist/actions/actions.service.js +19 -1
- package/dist/actions/actions.service.js.map +1 -1
- package/dist/app.module.js +2 -0
- package/dist/app.module.js.map +1 -1
- package/dist/extensions/actions/ai/mcp.binding.js +2 -0
- package/dist/extensions/actions/ai/mcp.binding.js.map +1 -1
- package/dist/extensions/actions/ai/memory.binding.js +2 -0
- package/dist/extensions/actions/ai/memory.binding.js.map +1 -1
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js +2 -0
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js.map +1 -1
- package/dist/extensions/actions/messaging/list.action.d.ts +2 -2
- package/dist/extensions/actions/messaging/list.action.js +7 -0
- package/dist/extensions/actions/messaging/list.action.js.map +1 -1
- package/dist/extensions/actions/subscriber/update-labels.action.js +3 -0
- package/dist/extensions/actions/subscriber/update-labels.action.js.map +1 -1
- package/dist/extensions/actions/workflow/call-workflow.action.d.ts +19 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js +83 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js.map +1 -0
- package/dist/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/dist/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/dist/extensions/index.d.ts +1 -0
- package/dist/extensions/index.js +1 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/tools/hexabot-mcp.tools.d.ts +58 -0
- package/dist/static/assets/{browser-ponyfill-DN-Mu1Dr.js → browser-ponyfill-jlQAUd0m.js} +1 -1
- package/dist/static/assets/{cssMode-BCWxI_Ux.js → cssMode-CmMO_Mso.js} +1 -1
- package/dist/static/assets/{freemarker2-CFvevPey.js → freemarker2-DcQVPFyh.js} +1 -1
- package/dist/static/assets/{handlebars-BCStA9Eg.js → handlebars-DcnzpFQC.js} +1 -1
- package/dist/static/assets/{html-DkIwCA7q.js → html-BFzOjuas.js} +1 -1
- package/dist/static/assets/{htmlMode-DHeMkUp8.js → htmlMode-C-vRCUKF.js} +1 -1
- package/dist/static/assets/{index-C6VeVxYx.js → index-BKczCDFq.js} +2103 -2103
- package/dist/static/assets/{javascript-DLPKJzLc.js → javascript-Cw6lfMAI.js} +1 -1
- package/dist/static/assets/{jsonMode-BUIDFK3O.js → jsonMode-5xhIbw7r.js} +1 -1
- package/dist/static/assets/{liquid-B0XmwdKh.js → liquid-DRY_tZKy.js} +1 -1
- package/dist/static/assets/{lspLanguageFeatures-Fcpw3Ev_.js → lspLanguageFeatures-g0jq7rI8.js} +1 -1
- package/dist/static/assets/{mdx-DmJKi-dU.js → mdx-Cj1GGNiS.js} +1 -1
- package/dist/static/assets/{python-B1WnWY4I.js → python-Cx_web_-.js} +1 -1
- package/dist/static/assets/{razor-CL2s-6qt.js → razor-Bf3Htwpg.js} +1 -1
- package/dist/static/assets/{tsMode-DSFhDcf0.js → tsMode-DcrJAKHJ.js} +1 -1
- package/dist/static/assets/{typescript-CmHv7Jwl.js → typescript-D_NLpP2s.js} +1 -1
- package/dist/static/assets/{xml-Cyy6zXDM.js → xml-DX7g-Nrp.js} +1 -1
- package/dist/static/assets/{yaml-D_D6TAHE.js → yaml-BO2WdsT4.js} +1 -1
- package/dist/static/index.html +1 -1
- package/dist/static/locales/en/translation.json +7 -1
- package/dist/static/locales/fr/translation.json +8 -2
- package/dist/transfer/adapters/content-type-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js +102 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/credential-transfer.adapter.d.ts +13 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js +110 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/label-transfer.adapter.d.ts +15 -0
- package/dist/transfer/adapters/label-transfer.adapter.js +189 -0
- package/dist/transfer/adapters/label-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.d.ts +16 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js +169 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js +111 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js.map +1 -0
- package/dist/transfer/index.d.ts +7 -0
- package/dist/transfer/index.js +24 -0
- package/dist/transfer/index.js.map +1 -0
- package/dist/transfer/workflow-transfer-adapter.registry.d.ts +27 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js +164 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js.map +1 -0
- package/dist/transfer/workflow-transfer-definition.service.d.ts +40 -0
- package/dist/transfer/workflow-transfer-definition.service.js +353 -0
- package/dist/transfer/workflow-transfer-definition.service.js.map +1 -0
- package/dist/transfer/workflow-transfer-resource-adapter.d.ts +33 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js +81 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js.map +1 -0
- package/dist/transfer/workflow-transfer.controller.d.ts +9 -0
- package/dist/transfer/workflow-transfer.controller.js +63 -0
- package/dist/transfer/workflow-transfer.controller.js.map +1 -0
- package/dist/transfer/workflow-transfer.module.d.ts +2 -0
- package/dist/transfer/workflow-transfer.module.js +47 -0
- package/dist/transfer/workflow-transfer.module.js.map +1 -0
- package/dist/transfer/workflow-transfer.service.d.ts +51 -0
- package/dist/transfer/workflow-transfer.service.js +509 -0
- package/dist/transfer/workflow-transfer.service.js.map +1 -0
- package/dist/transfer/workflow-transfer.types.d.ts +31 -0
- package/dist/transfer/workflow-transfer.types.js +35 -0
- package/dist/transfer/workflow-transfer.types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/user/dto/user.dto.d.ts +1 -1
- package/dist/utils/test/fixtures/label-group.d.ts +1 -1
- package/dist/utils/test/fixtures/label.d.ts +1 -1
- package/dist/utils/test/fixtures/subscriber.d.ts +1 -1
- package/dist/utils/test/fixtures/workflow-run.js +4 -1
- package/dist/utils/test/fixtures/workflow-run.js.map +1 -1
- package/dist/workflow/contexts/workflow-runtime.context.d.ts +3 -1
- package/dist/workflow/contexts/workflow-runtime.context.js +5 -0
- package/dist/workflow/contexts/workflow-runtime.context.js.map +1 -1
- package/dist/workflow/controllers/workflow-run.controller.js +2 -0
- package/dist/workflow/controllers/workflow-run.controller.js.map +1 -1
- package/dist/workflow/dto/workflow-run.dto.d.ts +1 -0
- package/dist/workflow/dto/workflow-run.dto.js +11 -0
- package/dist/workflow/dto/workflow-run.dto.js.map +1 -1
- package/dist/workflow/entities/memory-record.entity.d.ts +2 -0
- package/dist/workflow/entities/workflow-run.entity.d.ts +62 -0
- package/dist/workflow/entities/workflow-run.entity.js +15 -1
- package/dist/workflow/entities/workflow-run.entity.js.map +1 -1
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.js +7 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/repositories/workflow-run.repository.js +7 -1
- package/dist/workflow/repositories/workflow-run.repository.js.map +1 -1
- package/dist/workflow/resource-refs.d.ts +17 -0
- package/dist/workflow/resource-refs.js +15 -0
- package/dist/workflow/resource-refs.js.map +1 -0
- package/dist/workflow/services/agentic.service.d.ts +7 -1
- package/dist/workflow/services/agentic.service.js +134 -16
- package/dist/workflow/services/agentic.service.js.map +1 -1
- package/dist/workflow/services/workflow-run.service.d.ts +3 -0
- package/dist/workflow/services/workflow-run.service.js +75 -3
- package/dist/workflow/services/workflow-run.service.js.map +1 -1
- package/dist/workflow/services/workflow-version.service.d.ts +2 -0
- package/dist/workflow/services/workflow-version.service.js +19 -0
- package/dist/workflow/services/workflow-version.service.js.map +1 -1
- package/dist/workflow/services/workflow.service.d.ts +3 -1
- package/dist/workflow/services/workflow.service.js +5 -0
- package/dist/workflow/services/workflow.service.js.map +1 -1
- package/dist/workflow/types.d.ts +31 -0
- package/dist/workflow/types.js +2 -1
- package/dist/workflow/types.js.map +1 -1
- package/dist/workflow/workflow.module.js +3 -0
- package/dist/workflow/workflow.module.js.map +1 -1
- package/package.json +4 -4
- package/src/actions/actions.service.ts +34 -4
- package/src/app.module.ts +2 -0
- package/src/extensions/actions/ai/mcp.binding.ts +2 -0
- package/src/extensions/actions/ai/memory.binding.ts +2 -0
- package/src/extensions/actions/ai/retrieve-content-rag.action.ts +2 -0
- package/src/extensions/actions/messaging/list.action.ts +7 -0
- package/src/extensions/actions/subscriber/update-labels.action.ts +3 -0
- package/src/extensions/actions/workflow/call-workflow.action.ts +112 -0
- package/src/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/src/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/src/extensions/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/transfer/adapters/content-type-transfer.adapter.ts +154 -0
- package/src/transfer/adapters/credential-transfer.adapter.ts +158 -0
- package/src/transfer/adapters/label-transfer.adapter.ts +291 -0
- package/src/transfer/adapters/mcp-server-transfer.adapter.ts +255 -0
- package/src/transfer/adapters/memory-definition-transfer.adapter.ts +167 -0
- package/src/transfer/index.ts +19 -0
- package/src/transfer/workflow-transfer-adapter.registry.ts +250 -0
- package/src/transfer/workflow-transfer-definition.service.ts +587 -0
- package/src/transfer/workflow-transfer-resource-adapter.ts +132 -0
- package/src/transfer/workflow-transfer.controller.ts +86 -0
- package/src/transfer/workflow-transfer.module.ts +46 -0
- package/src/transfer/workflow-transfer.service.ts +858 -0
- package/src/transfer/workflow-transfer.types.ts +98 -0
- package/src/utils/test/fixtures/workflow-run.ts +4 -1
- package/src/workflow/contexts/workflow-runtime.context.ts +12 -1
- package/src/workflow/controllers/workflow-run.controller.ts +2 -0
- package/src/workflow/dto/workflow-run.dto.ts +10 -0
- package/src/workflow/entities/workflow-run.entity.ts +22 -1
- package/src/workflow/index.ts +14 -0
- package/src/workflow/repositories/workflow-run.repository.ts +7 -1
- package/src/workflow/resource-refs.ts +44 -0
- package/src/workflow/services/agentic.service.ts +256 -26
- package/src/workflow/services/workflow-run.service.ts +120 -7
- package/src/workflow/services/workflow-version.service.ts +33 -0
- package/src/workflow/services/workflow.service.ts +21 -1
- package/src/workflow/types.ts +52 -0
- package/src/workflow/workflow.module.ts +3 -0
|
@@ -0,0 +1,587 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2026 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
isTaskDefinition,
|
|
9
|
+
validateWorkflow,
|
|
10
|
+
type JsonValue,
|
|
11
|
+
type WorkflowDefinition,
|
|
12
|
+
} from '@hexabot-ai/agentic';
|
|
13
|
+
import { BadRequestException, Injectable } from '@nestjs/common';
|
|
14
|
+
import type { ZodTypeAny } from 'zod';
|
|
15
|
+
|
|
16
|
+
import { ActionService } from '@/actions/actions.service';
|
|
17
|
+
import { ActionName } from '@/actions/types';
|
|
18
|
+
import { RuntimeBindingsService } from '@/bindings/runtime-bindings.service';
|
|
19
|
+
import {
|
|
20
|
+
isWorkflowResourceRefKind,
|
|
21
|
+
WORKFLOW_RESOURCE_REF_METADATA_KEY,
|
|
22
|
+
type WorkflowActionResourceRefDescriptor,
|
|
23
|
+
type WorkflowResourceRefKind,
|
|
24
|
+
type WorkflowResourceRefMetadata,
|
|
25
|
+
type WorkflowSchemaResourceRefDescriptor,
|
|
26
|
+
} from '@/workflow/resource-refs';
|
|
27
|
+
|
|
28
|
+
export type WorkflowTaskResourceRefs = {
|
|
29
|
+
[kind: string]: string[];
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export type WorkflowTaskResourceIdMaps = Record<string, Record<string, string>>;
|
|
33
|
+
|
|
34
|
+
export type WorkflowBindingResourceRefs = WorkflowTaskResourceRefs;
|
|
35
|
+
|
|
36
|
+
type JsonSchemaResourceNode = {
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
$defs?: Record<string, JsonSchemaResourceNode>;
|
|
39
|
+
definitions?: Record<string, JsonSchemaResourceNode>;
|
|
40
|
+
$ref?: string;
|
|
41
|
+
properties?: Record<string, JsonSchemaResourceNode>;
|
|
42
|
+
items?: JsonSchemaResourceNode | JsonSchemaResourceNode[];
|
|
43
|
+
allOf?: JsonSchemaResourceNode[];
|
|
44
|
+
anyOf?: JsonSchemaResourceNode[];
|
|
45
|
+
oneOf?: JsonSchemaResourceNode[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
@Injectable()
|
|
49
|
+
export class WorkflowTransferDefinitionService {
|
|
50
|
+
constructor(
|
|
51
|
+
private readonly actionService: ActionService,
|
|
52
|
+
private readonly runtimeBindingsService: RuntimeBindingsService,
|
|
53
|
+
) {}
|
|
54
|
+
|
|
55
|
+
parseWithLocalCatalog(definitionYml: string): WorkflowDefinition {
|
|
56
|
+
const validation = validateWorkflow(definitionYml, {
|
|
57
|
+
bindingKinds: this.runtimeBindingsService.getRegistry(),
|
|
58
|
+
actions: Object.fromEntries(
|
|
59
|
+
Object.entries(this.actionService.getRegistry()).map(
|
|
60
|
+
([actionName, action]) => [
|
|
61
|
+
actionName,
|
|
62
|
+
{ supportedBindings: action.supportedBindings ?? [] },
|
|
63
|
+
],
|
|
64
|
+
),
|
|
65
|
+
),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
if (!validation.success) {
|
|
69
|
+
throw new BadRequestException(
|
|
70
|
+
`Invalid workflow YAML: ${validation.errors.join('; ')}`,
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return validation.data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
collectBindingResourceRefs(
|
|
78
|
+
definition: WorkflowDefinition,
|
|
79
|
+
): WorkflowBindingResourceRefs {
|
|
80
|
+
const refs = this.createResourceRefSet();
|
|
81
|
+
const bindingKinds = this.runtimeBindingsService.getRegistry();
|
|
82
|
+
|
|
83
|
+
for (const def of Object.values(definition.defs ?? {})) {
|
|
84
|
+
if (isTaskDefinition(def)) {
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const descriptor of this.getBindingResourceRefs(
|
|
89
|
+
bindingKinds,
|
|
90
|
+
def.kind,
|
|
91
|
+
)) {
|
|
92
|
+
this.addLiteralResourceRefs(
|
|
93
|
+
this.getResourceRefSet(refs, descriptor.kind),
|
|
94
|
+
this.getDefinitionValueAtPath(def.settings, descriptor.path),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return this.toResourceRefs(refs);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
remapBindingResourceRefs(
|
|
103
|
+
definition: WorkflowDefinition,
|
|
104
|
+
idMaps: WorkflowTaskResourceIdMaps,
|
|
105
|
+
): WorkflowDefinition {
|
|
106
|
+
let didChange = false;
|
|
107
|
+
const bindingKinds = this.runtimeBindingsService.getRegistry();
|
|
108
|
+
const nextDefs = Object.fromEntries(
|
|
109
|
+
Object.entries(definition.defs ?? {}).map(([defName, def]) => {
|
|
110
|
+
if (isTaskDefinition(def)) {
|
|
111
|
+
return [defName, def];
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
let nextDef = def;
|
|
115
|
+
for (const descriptor of this.getBindingResourceRefs(
|
|
116
|
+
bindingKinds,
|
|
117
|
+
def.kind,
|
|
118
|
+
)) {
|
|
119
|
+
const result = this.remapDefinitionValueAtPath(
|
|
120
|
+
nextDef.settings,
|
|
121
|
+
descriptor.path,
|
|
122
|
+
idMaps[descriptor.kind] ?? {},
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
if (result.didChange) {
|
|
126
|
+
didChange = true;
|
|
127
|
+
nextDef = {
|
|
128
|
+
...nextDef,
|
|
129
|
+
settings: result.value as Record<string, unknown>,
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return [defName, nextDef];
|
|
135
|
+
}),
|
|
136
|
+
) as WorkflowDefinition['defs'];
|
|
137
|
+
|
|
138
|
+
return didChange
|
|
139
|
+
? {
|
|
140
|
+
...definition,
|
|
141
|
+
defs: nextDefs,
|
|
142
|
+
}
|
|
143
|
+
: definition;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
collectTaskResourceRefs(
|
|
147
|
+
definition: WorkflowDefinition,
|
|
148
|
+
): WorkflowTaskResourceRefs {
|
|
149
|
+
const refs = this.createResourceRefSet();
|
|
150
|
+
const actions = this.actionService.getRegistry();
|
|
151
|
+
|
|
152
|
+
for (const def of Object.values(definition.defs ?? {})) {
|
|
153
|
+
if (!isTaskDefinition(def)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
for (const descriptor of this.getActionResourceRefs(
|
|
158
|
+
actions,
|
|
159
|
+
def.action,
|
|
160
|
+
)) {
|
|
161
|
+
this.addLiteralResourceRefs(
|
|
162
|
+
this.getResourceRefSet(refs, descriptor.kind),
|
|
163
|
+
this.getDefinitionValueAtPath(
|
|
164
|
+
descriptor.source === 'input' ? def.inputs : def.settings,
|
|
165
|
+
descriptor.path,
|
|
166
|
+
),
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
...this.toResourceRefs(refs),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
remapTaskResourceRefs(
|
|
177
|
+
definition: WorkflowDefinition,
|
|
178
|
+
idMaps: WorkflowTaskResourceIdMaps,
|
|
179
|
+
): WorkflowDefinition {
|
|
180
|
+
let didChange = false;
|
|
181
|
+
const actions = this.actionService.getRegistry();
|
|
182
|
+
const nextDefs = Object.fromEntries(
|
|
183
|
+
Object.entries(definition.defs ?? {}).map(([defName, def]) => {
|
|
184
|
+
if (!isTaskDefinition(def)) {
|
|
185
|
+
return [defName, def];
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
let nextDef = def;
|
|
189
|
+
for (const descriptor of this.getActionResourceRefs(
|
|
190
|
+
actions,
|
|
191
|
+
def.action,
|
|
192
|
+
)) {
|
|
193
|
+
const sourceKey =
|
|
194
|
+
descriptor.source === 'input' ? 'inputs' : 'settings';
|
|
195
|
+
const result = this.remapDefinitionValueAtPath(
|
|
196
|
+
nextDef[sourceKey],
|
|
197
|
+
descriptor.path,
|
|
198
|
+
idMaps[descriptor.kind] ?? {},
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
if (result.didChange) {
|
|
202
|
+
didChange = true;
|
|
203
|
+
nextDef = {
|
|
204
|
+
...nextDef,
|
|
205
|
+
[sourceKey]: result.value,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return [defName, nextDef];
|
|
211
|
+
}),
|
|
212
|
+
) as WorkflowDefinition['defs'];
|
|
213
|
+
|
|
214
|
+
return didChange
|
|
215
|
+
? {
|
|
216
|
+
...definition,
|
|
217
|
+
defs: nextDefs,
|
|
218
|
+
}
|
|
219
|
+
: definition;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
private getActionResourceRefs(
|
|
223
|
+
actions: ReturnType<ActionService['getRegistry']>,
|
|
224
|
+
actionName: string,
|
|
225
|
+
): readonly WorkflowActionResourceRefDescriptor[] {
|
|
226
|
+
const action = actions[actionName as ActionName];
|
|
227
|
+
if (!action) {
|
|
228
|
+
return [];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return [
|
|
232
|
+
...this.collectSchemaResourceRefs(action.inputSchema).map(
|
|
233
|
+
(descriptor) => ({
|
|
234
|
+
...descriptor,
|
|
235
|
+
source: 'input' as const,
|
|
236
|
+
}),
|
|
237
|
+
),
|
|
238
|
+
...this.collectSchemaResourceRefs(action.settingSchema).map(
|
|
239
|
+
(descriptor) => ({
|
|
240
|
+
...descriptor,
|
|
241
|
+
source: 'settings' as const,
|
|
242
|
+
}),
|
|
243
|
+
),
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private getBindingResourceRefs(
|
|
248
|
+
bindingKinds: ReturnType<RuntimeBindingsService['getRegistry']>,
|
|
249
|
+
bindingKind: string,
|
|
250
|
+
): readonly WorkflowSchemaResourceRefDescriptor[] {
|
|
251
|
+
const bindingDefinition = bindingKinds[bindingKind];
|
|
252
|
+
|
|
253
|
+
return bindingDefinition
|
|
254
|
+
? this.collectSchemaResourceRefs(bindingDefinition.schema)
|
|
255
|
+
: [];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
private collectSchemaResourceRefs(
|
|
259
|
+
schema: ZodTypeAny,
|
|
260
|
+
): readonly WorkflowSchemaResourceRefDescriptor[] {
|
|
261
|
+
const jsonSchema = schema.toJSONSchema({
|
|
262
|
+
target: 'draft-07',
|
|
263
|
+
}) as JsonSchemaResourceNode;
|
|
264
|
+
const refs: WorkflowSchemaResourceRefDescriptor[] = [];
|
|
265
|
+
|
|
266
|
+
this.visitJsonSchemaResourceRefs(jsonSchema, [], jsonSchema, refs);
|
|
267
|
+
|
|
268
|
+
return this.dedupeSchemaResourceRefs(refs);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
private visitJsonSchemaResourceRefs(
|
|
272
|
+
node: JsonSchemaResourceNode,
|
|
273
|
+
path: string[],
|
|
274
|
+
root: JsonSchemaResourceNode,
|
|
275
|
+
refs: WorkflowSchemaResourceRefDescriptor[],
|
|
276
|
+
seenRefs = new Set<string>(),
|
|
277
|
+
): void {
|
|
278
|
+
if (path.length > 0) {
|
|
279
|
+
for (const metadata of this.getResourceRefMetadata(node)) {
|
|
280
|
+
refs.push({
|
|
281
|
+
kind: metadata.kind,
|
|
282
|
+
path: path.join('.'),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (typeof node.$ref === 'string') {
|
|
288
|
+
if (seenRefs.has(node.$ref)) {
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const referencedNode = this.resolveJsonSchemaRef(root, node.$ref);
|
|
293
|
+
if (referencedNode) {
|
|
294
|
+
this.visitJsonSchemaResourceRefs(
|
|
295
|
+
referencedNode,
|
|
296
|
+
path,
|
|
297
|
+
root,
|
|
298
|
+
refs,
|
|
299
|
+
new Set([...seenRefs, node.$ref]),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
for (const [propertyName, propertyNode] of Object.entries(
|
|
307
|
+
node.properties ?? {},
|
|
308
|
+
)) {
|
|
309
|
+
this.visitJsonSchemaResourceRefs(
|
|
310
|
+
propertyNode,
|
|
311
|
+
[...path, propertyName],
|
|
312
|
+
root,
|
|
313
|
+
refs,
|
|
314
|
+
seenRefs,
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const arrayItems = node.items;
|
|
319
|
+
if (Array.isArray(arrayItems)) {
|
|
320
|
+
for (const itemNode of arrayItems) {
|
|
321
|
+
this.visitJsonSchemaResourceRefs(itemNode, path, root, refs, seenRefs);
|
|
322
|
+
}
|
|
323
|
+
} else if (arrayItems) {
|
|
324
|
+
this.visitJsonSchemaResourceRefs(arrayItems, path, root, refs, seenRefs);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
for (const variantKey of ['allOf', 'anyOf', 'oneOf'] as const) {
|
|
328
|
+
for (const variantNode of node[variantKey] ?? []) {
|
|
329
|
+
this.visitJsonSchemaResourceRefs(
|
|
330
|
+
variantNode,
|
|
331
|
+
path,
|
|
332
|
+
root,
|
|
333
|
+
refs,
|
|
334
|
+
seenRefs,
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
private getResourceRefMetadata(
|
|
341
|
+
node: JsonSchemaResourceNode,
|
|
342
|
+
): WorkflowResourceRefMetadata[] {
|
|
343
|
+
const metadata = node[WORKFLOW_RESOURCE_REF_METADATA_KEY];
|
|
344
|
+
const values = Array.isArray(metadata) ? metadata : [metadata];
|
|
345
|
+
|
|
346
|
+
return values.flatMap((value) => {
|
|
347
|
+
if (
|
|
348
|
+
typeof value === 'object' &&
|
|
349
|
+
value !== null &&
|
|
350
|
+
'kind' in value &&
|
|
351
|
+
isWorkflowResourceRefKind(value.kind)
|
|
352
|
+
) {
|
|
353
|
+
return [{ kind: value.kind }];
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return [];
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
private resolveJsonSchemaRef(
|
|
361
|
+
root: JsonSchemaResourceNode,
|
|
362
|
+
ref: string,
|
|
363
|
+
): JsonSchemaResourceNode | null {
|
|
364
|
+
if (!ref.startsWith('#/')) {
|
|
365
|
+
return null;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
return ref
|
|
369
|
+
.slice(2)
|
|
370
|
+
.split('/')
|
|
371
|
+
.map((segment) => {
|
|
372
|
+
return segment.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
373
|
+
})
|
|
374
|
+
.reduce<unknown>((current, segment) => {
|
|
375
|
+
return typeof current === 'object' &&
|
|
376
|
+
current !== null &&
|
|
377
|
+
segment in current
|
|
378
|
+
? (current as Record<string, unknown>)[segment]
|
|
379
|
+
: null;
|
|
380
|
+
}, root) as JsonSchemaResourceNode | null;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
private dedupeSchemaResourceRefs(
|
|
384
|
+
refs: WorkflowSchemaResourceRefDescriptor[],
|
|
385
|
+
): WorkflowSchemaResourceRefDescriptor[] {
|
|
386
|
+
const seen = new Set<string>();
|
|
387
|
+
|
|
388
|
+
return refs.filter((ref) => {
|
|
389
|
+
const key = `${ref.kind}:${ref.path}`;
|
|
390
|
+
if (seen.has(key)) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
seen.add(key);
|
|
395
|
+
|
|
396
|
+
return true;
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
private createResourceRefSet(): Record<string, Set<string>> {
|
|
401
|
+
return {};
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
private getResourceRefSet(
|
|
405
|
+
refs: Record<string, Set<string>>,
|
|
406
|
+
kind: WorkflowResourceRefKind,
|
|
407
|
+
): Set<string> {
|
|
408
|
+
refs[kind] ??= new Set<string>();
|
|
409
|
+
|
|
410
|
+
return refs[kind];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private toResourceRefs(
|
|
414
|
+
refs: Record<string, Set<string>>,
|
|
415
|
+
): WorkflowTaskResourceRefs {
|
|
416
|
+
return Object.fromEntries(
|
|
417
|
+
Object.entries(refs).map(([kind, ids]) => [kind, Array.from(ids)]),
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
private asRecord(value: unknown): Record<string, JsonValue> | null {
|
|
422
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
423
|
+
? (value as Record<string, JsonValue>)
|
|
424
|
+
: null;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
private getDefinitionValueAtPath(
|
|
428
|
+
value: unknown,
|
|
429
|
+
path: string,
|
|
430
|
+
): JsonValue | undefined {
|
|
431
|
+
let current: unknown = value;
|
|
432
|
+
for (const segment of this.parseResourcePath(path)) {
|
|
433
|
+
const record = this.asRecord(current);
|
|
434
|
+
if (!record || !(segment in record)) {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
current = record[segment];
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
return current as JsonValue | undefined;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
private parseResourcePath(path: string): string[] {
|
|
445
|
+
return path
|
|
446
|
+
.split('.')
|
|
447
|
+
.map((segment) => segment.trim())
|
|
448
|
+
.filter(Boolean);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private normalizeLiteralResourceId(value: unknown): string | null {
|
|
452
|
+
if (typeof value !== 'string') {
|
|
453
|
+
return null;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
const normalized = value.trim();
|
|
457
|
+
|
|
458
|
+
return normalized && !normalized.startsWith('=') ? normalized : null;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private addLiteralResourceRef(refs: Set<string>, value: unknown): void {
|
|
462
|
+
const ref = this.normalizeLiteralResourceId(value);
|
|
463
|
+
if (ref) {
|
|
464
|
+
refs.add(ref);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
private addLiteralResourceRefs(refs: Set<string>, value: unknown): void {
|
|
469
|
+
if (Array.isArray(value)) {
|
|
470
|
+
for (const item of value) {
|
|
471
|
+
this.addLiteralResourceRef(refs, item);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
this.addLiteralResourceRef(refs, value);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
private remapLiteralResourceValue(
|
|
481
|
+
value: JsonValue | undefined,
|
|
482
|
+
idMap: Record<string, string>,
|
|
483
|
+
): JsonValue | undefined {
|
|
484
|
+
const ref = this.normalizeLiteralResourceId(value);
|
|
485
|
+
|
|
486
|
+
return ref ? (idMap[ref] ?? value) : value;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
private remapLiteralResourceRefs(
|
|
490
|
+
value: JsonValue | undefined,
|
|
491
|
+
idMap: Record<string, string>,
|
|
492
|
+
): JsonValue | undefined {
|
|
493
|
+
return Array.isArray(value)
|
|
494
|
+
? this.remapLiteralResourceArray(value, idMap)
|
|
495
|
+
: this.remapLiteralResourceValue(value, idMap);
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
private remapLiteralResourceArray(
|
|
499
|
+
value: JsonValue | undefined,
|
|
500
|
+
idMap: Record<string, string>,
|
|
501
|
+
): JsonValue | undefined {
|
|
502
|
+
if (!Array.isArray(value)) {
|
|
503
|
+
return value;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
let didChange = false;
|
|
507
|
+
const nextValue: JsonValue[] = value.map((item) => {
|
|
508
|
+
const nextItem = this.remapLiteralResourceValue(item, idMap) ?? item;
|
|
509
|
+
if (nextItem !== item) {
|
|
510
|
+
didChange = true;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return nextItem;
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
return didChange ? nextValue : value;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private remapDefinitionValueAtPath(
|
|
520
|
+
value: unknown,
|
|
521
|
+
path: string,
|
|
522
|
+
idMap: Record<string, string>,
|
|
523
|
+
): { value: JsonValue | undefined; didChange: boolean } {
|
|
524
|
+
return this.remapDefinitionPathSegments(
|
|
525
|
+
value,
|
|
526
|
+
this.parseResourcePath(path),
|
|
527
|
+
idMap,
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
private remapDefinitionPathSegments(
|
|
532
|
+
value: unknown,
|
|
533
|
+
segments: string[],
|
|
534
|
+
idMap: Record<string, string>,
|
|
535
|
+
): { value: JsonValue | undefined; didChange: boolean } {
|
|
536
|
+
const [segment, ...tail] = segments;
|
|
537
|
+
if (!segment) {
|
|
538
|
+
return { value: value as JsonValue | undefined, didChange: false };
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const record = this.asRecord(value);
|
|
542
|
+
if (!record || !(segment in record)) {
|
|
543
|
+
return { value: value as JsonValue | undefined, didChange: false };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
if (tail.length === 0) {
|
|
547
|
+
const nextValue = this.remapLiteralResourceRefs(record[segment], idMap);
|
|
548
|
+
if (nextValue === record[segment]) {
|
|
549
|
+
return { value: value as JsonValue | undefined, didChange: false };
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
return {
|
|
553
|
+
value: this.withUpdatedPathValue(record, segment, nextValue),
|
|
554
|
+
didChange: true,
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const result = this.remapDefinitionPathSegments(
|
|
559
|
+
record[segment],
|
|
560
|
+
tail,
|
|
561
|
+
idMap,
|
|
562
|
+
);
|
|
563
|
+
if (!result.didChange) {
|
|
564
|
+
return { value: value as JsonValue | undefined, didChange: false };
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return {
|
|
568
|
+
value: this.withUpdatedPathValue(record, segment, result.value),
|
|
569
|
+
didChange: true,
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
private withUpdatedPathValue(
|
|
574
|
+
record: Record<string, JsonValue>,
|
|
575
|
+
segment: string,
|
|
576
|
+
value: JsonValue | undefined,
|
|
577
|
+
): JsonValue {
|
|
578
|
+
const nextRecord: Record<string, JsonValue> = { ...record };
|
|
579
|
+
if (value === undefined) {
|
|
580
|
+
delete nextRecord[segment];
|
|
581
|
+
} else {
|
|
582
|
+
nextRecord[segment] = value;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
return nextRecord;
|
|
586
|
+
}
|
|
587
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2026 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { WorkflowExportBundle } from '@hexabot-ai/types';
|
|
8
|
+
import { SetMetadata } from '@nestjs/common';
|
|
9
|
+
import { EntityManager } from 'typeorm';
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
type WorkflowTransferImportAdapterResult,
|
|
13
|
+
type WorkflowTransferResourceIdMaps,
|
|
14
|
+
} from './workflow-transfer.types';
|
|
15
|
+
|
|
16
|
+
export const WORKFLOW_TRANSFER_ADAPTER_METADATA_KEY =
|
|
17
|
+
'hexabot:workflowTransferAdapter';
|
|
18
|
+
|
|
19
|
+
export const WorkflowTransferAdapter = (): ClassDecorator =>
|
|
20
|
+
SetMetadata(WORKFLOW_TRANSFER_ADAPTER_METADATA_KEY, true);
|
|
21
|
+
|
|
22
|
+
export class WorkflowTransferExportContext {
|
|
23
|
+
private readonly refsByKind = new Map<string, Set<string>>();
|
|
24
|
+
|
|
25
|
+
constructor(refs: Record<string, string[]>) {
|
|
26
|
+
for (const [kind, ids] of Object.entries(refs)) {
|
|
27
|
+
this.addResourceRefs(kind, ids);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
getRefs(kind: string): string[] {
|
|
32
|
+
return Array.from(this.refsByKind.get(kind) ?? []);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
addResourceRefs(kind: string, ids: string[]): void {
|
|
36
|
+
const refs = this.getRefSet(kind);
|
|
37
|
+
for (const id of ids) {
|
|
38
|
+
refs.add(id);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
toRecord(): Record<string, string[]> {
|
|
43
|
+
return Object.fromEntries(
|
|
44
|
+
Array.from(this.refsByKind.entries()).map(([kind, ids]) => [
|
|
45
|
+
kind,
|
|
46
|
+
Array.from(ids),
|
|
47
|
+
]),
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
private getRefSet(kind: string): Set<string> {
|
|
52
|
+
const existing = this.refsByKind.get(kind);
|
|
53
|
+
if (existing) {
|
|
54
|
+
return existing;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const refs = new Set<string>();
|
|
58
|
+
this.refsByKind.set(kind, refs);
|
|
59
|
+
|
|
60
|
+
return refs;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export class WorkflowTransferImportContext {
|
|
65
|
+
private readonly resources: Record<string, unknown[]>;
|
|
66
|
+
|
|
67
|
+
private readonly resultsByKind = new Map<
|
|
68
|
+
string,
|
|
69
|
+
WorkflowTransferImportAdapterResult
|
|
70
|
+
>();
|
|
71
|
+
|
|
72
|
+
constructor(
|
|
73
|
+
public readonly manager: EntityManager,
|
|
74
|
+
resources: WorkflowExportBundle['resources'],
|
|
75
|
+
public readonly ownerId: string,
|
|
76
|
+
) {
|
|
77
|
+
this.resources = resources as Record<string, unknown[]>;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getResources<Resource>(key: string): Resource[] {
|
|
81
|
+
const resources = this.resources[key];
|
|
82
|
+
|
|
83
|
+
return Array.isArray(resources) ? (resources as Resource[]) : [];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
getIdMap(kind: string): Record<string, string> {
|
|
87
|
+
return this.resultsByKind.get(kind)?.idMap ?? {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getDependencyResult(
|
|
91
|
+
kind: string,
|
|
92
|
+
): WorkflowTransferImportAdapterResult | undefined {
|
|
93
|
+
return this.resultsByKind.get(kind);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
addResult(kind: string, result: WorkflowTransferImportAdapterResult): void {
|
|
97
|
+
this.resultsByKind.set(kind, result);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getIdMaps(): WorkflowTransferResourceIdMaps {
|
|
101
|
+
return Object.fromEntries(
|
|
102
|
+
Array.from(this.resultsByKind.entries()).map(([kind, result]) => [
|
|
103
|
+
kind,
|
|
104
|
+
result.idMap,
|
|
105
|
+
]),
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
getResultsInOrder(kinds: string[]): WorkflowTransferImportAdapterResult[] {
|
|
110
|
+
return kinds.flatMap((kind) => {
|
|
111
|
+
const result = this.resultsByKind.get(kind);
|
|
112
|
+
|
|
113
|
+
return result ? [result] : [];
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export abstract class WorkflowTransferResourceAdapter {
|
|
119
|
+
abstract readonly kind: string;
|
|
120
|
+
|
|
121
|
+
abstract readonly resourceKeys: readonly string[];
|
|
122
|
+
|
|
123
|
+
readonly dependsOn: readonly string[] = [];
|
|
124
|
+
|
|
125
|
+
abstract buildExportResources(
|
|
126
|
+
ctx: WorkflowTransferExportContext,
|
|
127
|
+
): Promise<Record<string, unknown[]>>;
|
|
128
|
+
|
|
129
|
+
abstract importResources(
|
|
130
|
+
ctx: WorkflowTransferImportContext,
|
|
131
|
+
): Promise<WorkflowTransferImportAdapterResult>;
|
|
132
|
+
}
|