@hexabot-ai/api 3.2.2-alpha.14 → 3.2.2-alpha.16

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 (89) hide show
  1. package/dist/mcp/mcp-api.module.js +2 -2
  2. package/dist/mcp/mcp-api.module.js.map +1 -1
  3. package/dist/mcp/tools/catalog-mcp.tools.d.ts +63 -0
  4. package/dist/mcp/tools/catalog-mcp.tools.js +135 -0
  5. package/dist/mcp/tools/catalog-mcp.tools.js.map +1 -0
  6. package/dist/mcp/tools/cms-mcp.tools.d.ts +128 -0
  7. package/dist/mcp/tools/cms-mcp.tools.js +242 -0
  8. package/dist/mcp/tools/cms-mcp.tools.js.map +1 -0
  9. package/dist/mcp/tools/credential-mcp.tools.d.ts +19 -0
  10. package/dist/mcp/tools/credential-mcp.tools.js +82 -0
  11. package/dist/mcp/tools/credential-mcp.tools.js.map +1 -0
  12. package/dist/mcp/tools/hexabot-mcp-tool.base.d.ts +22 -0
  13. package/dist/mcp/tools/hexabot-mcp-tool.base.js +48 -0
  14. package/dist/mcp/tools/hexabot-mcp-tool.base.js.map +1 -0
  15. package/dist/mcp/tools/hexabot-mcp.schemas.d.ts +41 -0
  16. package/dist/mcp/tools/hexabot-mcp.schemas.js +36 -0
  17. package/dist/mcp/tools/hexabot-mcp.schemas.js.map +1 -0
  18. package/dist/mcp/tools/hexabot-mcp.tools.d.ts +1 -1329
  19. package/dist/mcp/tools/hexabot-mcp.tools.js +14 -1097
  20. package/dist/mcp/tools/hexabot-mcp.tools.js.map +1 -1
  21. package/dist/mcp/tools/hexabot-mcp.utils.d.ts +1 -0
  22. package/dist/mcp/tools/hexabot-mcp.utils.js +9 -0
  23. package/dist/mcp/tools/hexabot-mcp.utils.js.map +1 -0
  24. package/dist/mcp/tools/index.d.ts +22 -0
  25. package/dist/mcp/tools/index.js +50 -0
  26. package/dist/mcp/tools/index.js.map +1 -0
  27. package/dist/mcp/tools/mcp-server-mcp.tools.d.ts +77 -0
  28. package/dist/mcp/tools/mcp-server-mcp.tools.js +130 -0
  29. package/dist/mcp/tools/mcp-server-mcp.tools.js.map +1 -0
  30. package/dist/mcp/tools/memory-definition-mcp.tools.d.ts +57 -0
  31. package/dist/mcp/tools/memory-definition-mcp.tools.js +128 -0
  32. package/dist/mcp/tools/memory-definition-mcp.tools.js.map +1 -0
  33. package/dist/mcp/tools/workflow-mcp.helper.d.ts +141 -0
  34. package/dist/mcp/tools/workflow-mcp.helper.js +108 -0
  35. package/dist/mcp/tools/workflow-mcp.helper.js.map +1 -0
  36. package/dist/mcp/tools/workflow-mcp.tools.d.ts +468 -0
  37. package/dist/mcp/tools/workflow-mcp.tools.js +222 -0
  38. package/dist/mcp/tools/workflow-mcp.tools.js.map +1 -0
  39. package/dist/mcp/tools/workflow-run-mcp.tools.d.ts +347 -0
  40. package/dist/mcp/tools/workflow-run-mcp.tools.js +229 -0
  41. package/dist/mcp/tools/workflow-run-mcp.tools.js.map +1 -0
  42. package/dist/mcp/tools/workflow-version-mcp.tools.d.ts +230 -0
  43. package/dist/mcp/tools/workflow-version-mcp.tools.js +211 -0
  44. package/dist/mcp/tools/workflow-version-mcp.tools.js.map +1 -0
  45. package/dist/static/assets/{browser-ponyfill-D3QB7SUD.js → browser-ponyfill-CBXq65SP.js} +1 -1
  46. package/dist/static/assets/{cssMode-Bk0-m_Ij.js → cssMode-CN8f6H-j.js} +1 -1
  47. package/dist/static/assets/{freemarker2-D1tTqqt8.js → freemarker2-Dfrb95Lt.js} +1 -1
  48. package/dist/static/assets/{handlebars-D3VQYLDj.js → handlebars-B2cershf.js} +1 -1
  49. package/dist/static/assets/{html-Cmhr3-6_.js → html-CfjuUmTR.js} +1 -1
  50. package/dist/static/assets/{htmlMode-Bb5Kynv_.js → htmlMode-pl8CUTaA.js} +1 -1
  51. package/dist/static/assets/{index-JPQj6OiL.js → index-Cq8K5Poc.js} +2131 -2131
  52. package/dist/static/assets/{index-BI1BtkYv.css → index-D-b9KTZd.css} +1 -1
  53. package/dist/static/assets/{javascript-BkQdNyFT.js → javascript-Be45wN2i.js} +1 -1
  54. package/dist/static/assets/{jsonMode-CS1296LF.js → jsonMode-yPU1rg8t.js} +1 -1
  55. package/dist/static/assets/{liquid-C9ufUE-r.js → liquid-BMJibvBr.js} +1 -1
  56. package/dist/static/assets/{lspLanguageFeatures-Dxd2SsrE.js → lspLanguageFeatures-Cxz6b29K.js} +1 -1
  57. package/dist/static/assets/{mdx-Dm4bC48D.js → mdx-DpvXygi3.js} +1 -1
  58. package/dist/static/assets/{python-ZvBWpr_q.js → python-BHEnNcMm.js} +1 -1
  59. package/dist/static/assets/{razor-DBj-LyWR.js → razor-B6-tNhh_.js} +1 -1
  60. package/dist/static/assets/{tsMode-DzMlJB2i.js → tsMode-J4SJIY5C.js} +1 -1
  61. package/dist/static/assets/{typescript-Cfo3bzpy.js → typescript-Df82IAgp.js} +1 -1
  62. package/dist/static/assets/{xml-ZyceR0L3.js → xml-C8jr31yy.js} +1 -1
  63. package/dist/static/assets/{yaml-oyu_gOar.js → yaml-BEd1RzBW.js} +1 -1
  64. package/dist/static/index.html +2 -2
  65. package/dist/tsconfig.build.tsbuildinfo +1 -1
  66. package/dist/websocket/websocket.gateway.d.ts +2 -1
  67. package/dist/websocket/websocket.gateway.js.map +1 -1
  68. package/dist/workflow/services/workflow.service.d.ts +9 -8
  69. package/dist/workflow/services/workflow.service.js +50 -35
  70. package/dist/workflow/services/workflow.service.js.map +1 -1
  71. package/package.json +4 -4
  72. package/src/mcp/README.md +12 -2
  73. package/src/mcp/mcp-api.module.ts +2 -2
  74. package/src/mcp/tools/catalog-mcp.tools.ts +122 -0
  75. package/src/mcp/tools/cms-mcp.tools.ts +239 -0
  76. package/src/mcp/tools/credential-mcp.tools.ts +81 -0
  77. package/src/mcp/tools/hexabot-mcp-tool.base.ts +74 -0
  78. package/src/mcp/tools/hexabot-mcp.schemas.ts +55 -0
  79. package/src/mcp/tools/hexabot-mcp.tools.ts +1 -1194
  80. package/src/mcp/tools/hexabot-mcp.utils.ts +13 -0
  81. package/src/mcp/tools/index.ts +51 -0
  82. package/src/mcp/tools/mcp-server-mcp.tools.ts +135 -0
  83. package/src/mcp/tools/memory-definition-mcp.tools.ts +125 -0
  84. package/src/mcp/tools/workflow-mcp.helper.ts +166 -0
  85. package/src/mcp/tools/workflow-mcp.tools.ts +254 -0
  86. package/src/mcp/tools/workflow-run-mcp.tools.ts +274 -0
  87. package/src/mcp/tools/workflow-version-mcp.tools.ts +226 -0
  88. package/src/websocket/websocket.gateway.ts +2 -1
  89. package/src/workflow/services/workflow.service.ts +58 -30
@@ -0,0 +1,13 @@
1
+ /*
2
+ * Hexabot — Fair Core License (FCL-1.0-ALv2)
3
+ * Copyright (c) 2026 Hexastack.
4
+ * Full terms: see LICENSE.md.
5
+ */
6
+
7
+ export const sanitizeCredential = <T extends Record<string, unknown>>(
8
+ credential: T,
9
+ ): T => {
10
+ const { value: _value, ...safeCredential } = credential;
11
+
12
+ return safeCredential as T;
13
+ };
@@ -0,0 +1,51 @@
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 { HexabotCatalogMcpTools } from './catalog-mcp.tools';
8
+ import { HexabotCmsMcpTools } from './cms-mcp.tools';
9
+ import { HexabotCredentialMcpTools } from './credential-mcp.tools';
10
+ import { HexabotMcpServerTools } from './mcp-server-mcp.tools';
11
+ import { HexabotMemoryDefinitionMcpTools } from './memory-definition-mcp.tools';
12
+ import { HexabotWorkflowMcpHelper } from './workflow-mcp.helper';
13
+ import { HexabotWorkflowMcpTools } from './workflow-mcp.tools';
14
+ import { HexabotWorkflowRunMcpTools } from './workflow-run-mcp.tools';
15
+ import { HexabotWorkflowVersionMcpTools } from './workflow-version-mcp.tools';
16
+
17
+ export * from './catalog-mcp.tools';
18
+
19
+ export * from './cms-mcp.tools';
20
+
21
+ export * from './credential-mcp.tools';
22
+
23
+ export * from './hexabot-mcp-tool.base';
24
+
25
+ export * from './hexabot-mcp.schemas';
26
+
27
+ export * from './hexabot-mcp.utils';
28
+
29
+ export * from './memory-definition-mcp.tools';
30
+
31
+ export * from './mcp-server-mcp.tools';
32
+
33
+ export * from './workflow-mcp.helper';
34
+
35
+ export * from './workflow-mcp.tools';
36
+
37
+ export * from './workflow-run-mcp.tools';
38
+
39
+ export * from './workflow-version-mcp.tools';
40
+
41
+ export const HEXABOT_MCP_TOOL_PROVIDERS = [
42
+ HexabotWorkflowMcpHelper,
43
+ HexabotWorkflowMcpTools,
44
+ HexabotWorkflowVersionMcpTools,
45
+ HexabotWorkflowRunMcpTools,
46
+ HexabotMemoryDefinitionMcpTools,
47
+ HexabotCatalogMcpTools,
48
+ HexabotCredentialMcpTools,
49
+ HexabotMcpServerTools,
50
+ HexabotCmsMcpTools,
51
+ ];
@@ -0,0 +1,135 @@
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 { Action } from '@hexabot-ai/types';
8
+ import { Injectable, NotFoundException } from '@nestjs/common';
9
+ import { Tool, ToolGuards } from '@rekog/mcp-nest';
10
+ import { z } from 'zod';
11
+
12
+ import { McpServerOrmEntity } from '@/workflow/entities/mcp-server.entity';
13
+ import { McpServerService } from '@/workflow/services/mcp-server.service';
14
+ import { McpServerTransport } from '@/workflow/types';
15
+
16
+ import { McpPermission } from '../decorators/mcp-permission.decorator';
17
+ import { McpPermissionGuard } from '../guards/mcp-permission.guard';
18
+
19
+ import { HexabotMcpToolBase } from './hexabot-mcp-tool.base';
20
+ import {
21
+ mcpServerPayloadSchema,
22
+ PaginationArgs,
23
+ paginationSchema,
24
+ uuidSchema,
25
+ } from './hexabot-mcp.schemas';
26
+
27
+ @Injectable()
28
+ export class HexabotMcpServerTools extends HexabotMcpToolBase {
29
+ constructor(private readonly mcpServerService: McpServerService) {
30
+ super();
31
+ }
32
+
33
+ @McpPermission('mcpserver', Action.READ)
34
+ @ToolGuards([McpPermissionGuard])
35
+ @Tool({
36
+ name: 'hexabot_mcp_server_search',
37
+ description: 'Search configured MCP servers.',
38
+ parameters: z.object({
39
+ query: z.string().optional(),
40
+ enabled: z.boolean().optional(),
41
+ transport: z.enum(McpServerTransport).optional(),
42
+ credentialId: uuidSchema.optional(),
43
+ ...paginationSchema,
44
+ }),
45
+ })
46
+ async searchMcpServers(
47
+ args: {
48
+ query?: string;
49
+ enabled?: boolean;
50
+ transport?: McpServerTransport;
51
+ credentialId?: string;
52
+ } & PaginationArgs,
53
+ ) {
54
+ const base = {
55
+ ...(args.enabled !== undefined ? { enabled: args.enabled } : {}),
56
+ ...(args.transport ? { transport: args.transport } : {}),
57
+ ...(args.credentialId ? { credential: { id: args.credentialId } } : {}),
58
+ };
59
+ const where = args.query
60
+ ? [
61
+ { ...base, name: this.contains(args.query) },
62
+ { ...base, url: this.contains(args.query) },
63
+ { ...base, command: this.contains(args.query) },
64
+ ]
65
+ : base;
66
+
67
+ return await this.listWithCount(
68
+ this.mcpServerService,
69
+ this.findOptions<McpServerOrmEntity>(args, where as any),
70
+ );
71
+ }
72
+
73
+ @McpPermission('mcpserver', Action.READ)
74
+ @ToolGuards([McpPermissionGuard])
75
+ @Tool({
76
+ name: 'hexabot_mcp_server_get',
77
+ description: 'Read one configured MCP server.',
78
+ parameters: z.object({
79
+ id: uuidSchema,
80
+ }),
81
+ })
82
+ async getMcpServer(args: { id: string }) {
83
+ const server = await this.mcpServerService.findOneAndPopulate(args.id);
84
+ if (!server) {
85
+ throw new NotFoundException(`MCP server ${args.id} not found`);
86
+ }
87
+
88
+ return server;
89
+ }
90
+
91
+ @McpPermission('mcpserver', Action.CREATE)
92
+ @ToolGuards([McpPermissionGuard])
93
+ @Tool({
94
+ name: 'hexabot_mcp_server_create',
95
+ description: 'Create an MCP server configuration.',
96
+ parameters: z.object({
97
+ ...mcpServerPayloadSchema,
98
+ name: z.string().min(1),
99
+ }),
100
+ })
101
+ async createMcpServer(args: {
102
+ name: string;
103
+ enabled?: boolean;
104
+ transport?: McpServerTransport;
105
+ url?: string | null;
106
+ command?: string | null;
107
+ args?: string[] | null;
108
+ cwd?: string | null;
109
+ credential?: string | null;
110
+ }) {
111
+ return await this.mcpServerService.create({
112
+ enabled: true,
113
+ transport: McpServerTransport.http,
114
+ ...args,
115
+ } as any);
116
+ }
117
+
118
+ @McpPermission('mcpserver', Action.UPDATE)
119
+ @ToolGuards([McpPermissionGuard])
120
+ @Tool({
121
+ name: 'hexabot_mcp_server_update',
122
+ description: 'Update an MCP server configuration.',
123
+ parameters: z.object({
124
+ id: uuidSchema,
125
+ ...mcpServerPayloadSchema,
126
+ }),
127
+ })
128
+ async updateMcpServer(args: { id: string } & Record<string, unknown>) {
129
+ const { id, ...updates } = args;
130
+
131
+ await this.getMcpServer({ id });
132
+
133
+ return await this.mcpServerService.updateOne(id, updates as any);
134
+ }
135
+ }
@@ -0,0 +1,125 @@
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 { Action } from '@hexabot-ai/types';
8
+ import { Injectable, NotFoundException } from '@nestjs/common';
9
+ import { Tool, ToolGuards } from '@rekog/mcp-nest';
10
+ import { z } from 'zod';
11
+
12
+ import { MemoryDefinitionService } from '@/workflow/services/memory-definition.service';
13
+ import { MemoryScope } from '@/workflow/types';
14
+
15
+ import { McpPermission } from '../decorators/mcp-permission.decorator';
16
+ import { McpPermissionGuard } from '../guards/mcp-permission.guard';
17
+
18
+ import { HexabotMcpToolBase } from './hexabot-mcp-tool.base';
19
+ import {
20
+ jsonObjectSchema,
21
+ PaginationArgs,
22
+ paginationSchema,
23
+ uuidSchema,
24
+ } from './hexabot-mcp.schemas';
25
+
26
+ @Injectable()
27
+ export class HexabotMemoryDefinitionMcpTools extends HexabotMcpToolBase {
28
+ constructor(
29
+ private readonly memoryDefinitionService: MemoryDefinitionService,
30
+ ) {
31
+ super();
32
+ }
33
+
34
+ @McpPermission('memorydefinition', Action.READ)
35
+ @ToolGuards([McpPermissionGuard])
36
+ @Tool({
37
+ name: 'hexabot_memory_definition_search',
38
+ description: 'Search workflow memory definitions.',
39
+ parameters: z.object({
40
+ query: z.string().optional(),
41
+ scope: z.enum(MemoryScope).optional(),
42
+ ...paginationSchema,
43
+ }),
44
+ })
45
+ async searchMemoryDefinitions(
46
+ args: { query?: string; scope?: MemoryScope } & PaginationArgs,
47
+ ) {
48
+ const base = args.scope ? { scope: args.scope } : {};
49
+ const where = args.query
50
+ ? [
51
+ { ...base, name: this.contains(args.query) },
52
+ { ...base, slug: this.contains(args.query) },
53
+ ]
54
+ : base;
55
+
56
+ return await this.listWithCount(
57
+ this.memoryDefinitionService,
58
+ this.findOptions(args, where),
59
+ );
60
+ }
61
+
62
+ @McpPermission('memorydefinition', Action.READ)
63
+ @ToolGuards([McpPermissionGuard])
64
+ @Tool({
65
+ name: 'hexabot_memory_definition_get',
66
+ description: 'Read one workflow memory definition.',
67
+ parameters: z.object({
68
+ id: uuidSchema,
69
+ }),
70
+ })
71
+ async getMemoryDefinition(args: { id: string }) {
72
+ const definition = await this.memoryDefinitionService.findOne(args.id);
73
+ if (!definition) {
74
+ throw new NotFoundException(`Memory definition ${args.id} not found`);
75
+ }
76
+
77
+ return definition;
78
+ }
79
+
80
+ @McpPermission('memorydefinition', Action.CREATE)
81
+ @ToolGuards([McpPermissionGuard])
82
+ @Tool({
83
+ name: 'hexabot_memory_definition_create',
84
+ description: 'Create a workflow memory definition.',
85
+ parameters: z.object({
86
+ name: z.string().min(1),
87
+ slug: z.string().regex(/^[a-z0-9_]+$/),
88
+ scope: z.enum(MemoryScope),
89
+ schema: jsonObjectSchema,
90
+ ttlSeconds: z.number().int().min(1).nullable().optional(),
91
+ }),
92
+ })
93
+ async createMemoryDefinition(args: {
94
+ name: string;
95
+ slug: string;
96
+ scope: MemoryScope;
97
+ schema: Record<string, unknown>;
98
+ ttlSeconds?: number | null;
99
+ }) {
100
+ return await this.memoryDefinitionService.create(args as any);
101
+ }
102
+
103
+ @McpPermission('memorydefinition', Action.UPDATE)
104
+ @ToolGuards([McpPermissionGuard])
105
+ @Tool({
106
+ name: 'hexabot_memory_definition_update',
107
+ description: 'Update a workflow memory definition.',
108
+ parameters: z.object({
109
+ id: uuidSchema,
110
+ name: z.string().min(1).optional(),
111
+ slug: z
112
+ .string()
113
+ .regex(/^[a-z0-9_]+$/)
114
+ .optional(),
115
+ scope: z.enum(MemoryScope).optional(),
116
+ schema: jsonObjectSchema.optional(),
117
+ ttlSeconds: z.number().int().min(1).nullable().optional(),
118
+ }),
119
+ })
120
+ async updateMemoryDefinition(args: { id: string } & Record<string, unknown>) {
121
+ const { id, ...updates } = args;
122
+
123
+ return await this.memoryDefinitionService.updateOne(id, updates as any);
124
+ }
125
+ }
@@ -0,0 +1,166 @@
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 { type WorkflowFull, type WorkflowVersion } from '@hexabot-ai/types';
8
+ import {
9
+ BadRequestException,
10
+ Injectable,
11
+ NotFoundException,
12
+ } from '@nestjs/common';
13
+
14
+ import { WorkflowNewVersionDto } from '@/workflow/dto/workflow-version.dto';
15
+ import { parseWorkflowDefinition } from '@/workflow/lib/workflow-definition';
16
+ import { WorkflowVersionService } from '@/workflow/services/workflow-version.service';
17
+ import { WorkflowService } from '@/workflow/services/workflow.service';
18
+ import { WorkflowVersionAction } from '@/workflow/types';
19
+
20
+ type WorkflowVersionSummary = Pick<
21
+ WorkflowVersion,
22
+ | 'id'
23
+ | 'version'
24
+ | 'checksum'
25
+ | 'message'
26
+ | 'action'
27
+ | 'parentVersion'
28
+ | 'workflow'
29
+ | 'createdBy'
30
+ | 'createdAt'
31
+ | 'updatedAt'
32
+ >;
33
+
34
+ @Injectable()
35
+ export class HexabotWorkflowMcpHelper {
36
+ constructor(
37
+ private readonly workflowService: WorkflowService,
38
+ private readonly workflowVersionService: WorkflowVersionService,
39
+ ) {}
40
+
41
+ async requireWorkflow(id: string) {
42
+ const workflow = await this.workflowService.findOneAndPopulate(id);
43
+ if (!workflow) {
44
+ throw new NotFoundException(`Workflow ${id} not found`);
45
+ }
46
+
47
+ return workflow;
48
+ }
49
+
50
+ async commitWorkflowDefinition(params: {
51
+ workflowId: string;
52
+ definitionYml: string;
53
+ message?: string | null;
54
+ parentVersion?: string | null;
55
+ action?: WorkflowVersionAction;
56
+ createdBy: string;
57
+ }) {
58
+ const workflow = await this.requireWorkflow(params.workflowId);
59
+ try {
60
+ parseWorkflowDefinition(params.definitionYml);
61
+ } catch (error) {
62
+ throw new BadRequestException(
63
+ error instanceof Error ? error.message : 'Invalid workflow YAML',
64
+ );
65
+ }
66
+ const parentVersion =
67
+ params.parentVersion === undefined
68
+ ? this.resolveRelationId(workflow.currentVersion)
69
+ : params.parentVersion;
70
+ const payload: WorkflowNewVersionDto = {
71
+ workflow: params.workflowId,
72
+ definitionYml: params.definitionYml,
73
+ message: params.message ?? undefined,
74
+ parentVersion: parentVersion ?? undefined,
75
+ action: params.action ?? WorkflowVersionAction.update,
76
+ createdBy: params.createdBy,
77
+ };
78
+
79
+ return await this.workflowVersionService.commit(payload);
80
+ }
81
+
82
+ async restoreWorkflowVersionSnapshot(
83
+ args: { workflowId: string; versionId: string; message?: string },
84
+ updatedBy: string,
85
+ ) {
86
+ return await this.workflowVersionService.restoreVersion(
87
+ args.workflowId,
88
+ args.versionId,
89
+ {
90
+ updatedBy,
91
+ message: args.message,
92
+ },
93
+ );
94
+ }
95
+
96
+ buildWorkflowVersionStatus(workflow: WorkflowFull) {
97
+ const currentVersion = this.summarizeWorkflowVersion(
98
+ workflow.currentVersion,
99
+ );
100
+ const publishedVersion = this.summarizeWorkflowVersion(
101
+ workflow.publishedVersion,
102
+ );
103
+ const currentVersionId = currentVersion?.id ?? null;
104
+ const publishedVersionId = publishedVersion?.id ?? null;
105
+
106
+ return {
107
+ workflow: {
108
+ id: workflow.id,
109
+ name: workflow.name,
110
+ type: workflow.type,
111
+ },
112
+ currentVersion,
113
+ publishedVersion,
114
+ currentVersionId,
115
+ publishedVersionId,
116
+ isPublished: Boolean(publishedVersionId),
117
+ isCurrentVersionPublished:
118
+ Boolean(currentVersionId) && currentVersionId === publishedVersionId,
119
+ hasUnpublishedChanges:
120
+ Boolean(currentVersionId) && currentVersionId !== publishedVersionId,
121
+ };
122
+ }
123
+
124
+ private summarizeWorkflowVersion(
125
+ version: WorkflowVersion | null | undefined,
126
+ ): WorkflowVersionSummary | null {
127
+ if (!version) {
128
+ return null;
129
+ }
130
+ const {
131
+ id,
132
+ version: versionNumber,
133
+ checksum,
134
+ message,
135
+ action,
136
+ parentVersion,
137
+ workflow,
138
+ createdBy,
139
+ createdAt,
140
+ updatedAt,
141
+ } = version;
142
+
143
+ return {
144
+ id,
145
+ version: versionNumber,
146
+ checksum,
147
+ message,
148
+ action,
149
+ parentVersion,
150
+ workflow,
151
+ createdBy,
152
+ createdAt,
153
+ updatedAt,
154
+ };
155
+ }
156
+
157
+ private resolveRelationId(
158
+ relation: string | { id?: string | null } | null | undefined,
159
+ ): string | null {
160
+ if (typeof relation === 'string') {
161
+ return relation;
162
+ }
163
+
164
+ return relation?.id ?? null;
165
+ }
166
+ }