plugin-agent-orchestrator 1.0.21 → 1.0.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/dist/client/index.js +1 -1
  4. package/dist/client-v2/214.723affb37c13bf7a.js +10 -0
  5. package/dist/client-v2/264.0533912e6c5ea2d7.js +10 -0
  6. package/dist/client-v2/41.1805b2edfaa4afe2.js +10 -0
  7. package/dist/client-v2/418.5ae055abf141820e.js +10 -0
  8. package/dist/client-v2/619.d99d3c9e61c99064.js +10 -0
  9. package/dist/client-v2/70.a15d7fcec7c41768.js +10 -0
  10. package/dist/client-v2/892.72db4161511c8a16.js +10 -0
  11. package/dist/client-v2/926.87f660b670d85bcc.js +10 -0
  12. package/dist/client-v2/index.js +10 -0
  13. package/dist/externalVersion.js +7 -6
  14. package/dist/locale/en-US.json +7 -0
  15. package/dist/locale/vi-VN.json +7 -0
  16. package/dist/locale/zh-CN.json +27 -0
  17. package/dist/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.js +63 -0
  18. package/dist/server/plugin.js +41 -1
  19. package/dist/server/services/AgentHarness.js +52 -27
  20. package/dist/server/services/AgentLoopController.js +8 -2
  21. package/dist/server/services/AgentLoopService.js +1 -1
  22. package/dist/server/services/AgentRegistryService.js +53 -42
  23. package/dist/server/services/CircuitBreaker.js +7 -2
  24. package/dist/server/services/CodeValidator.js +48 -14
  25. package/dist/server/services/SandboxRunner.js +18 -14
  26. package/dist/server/skill-hub/plugin.js +44 -17
  27. package/dist/server/tools/delegate-task.js +7 -2
  28. package/dist/server/tools/skill-execute.js +33 -2
  29. package/dist/server/utils/ai-manager.js +51 -0
  30. package/dist/server/utils/ctx-utils.js +11 -0
  31. package/dist/server/utils/skill-settings.js +122 -0
  32. package/package.json +49 -45
  33. package/src/client/AIEmployeesContext.tsx +51 -14
  34. package/src/client/AgentRunsTab.tsx +767 -764
  35. package/src/client/HarnessProfilesTab.tsx +254 -247
  36. package/src/client/RulesTab.tsx +780 -716
  37. package/src/client/TracingTab.tsx +1 -0
  38. package/src/client/plugin.tsx +34 -27
  39. package/src/client/skill-hub/components/GitSkillImport.tsx +10 -3
  40. package/src/client/skill-hub/components/SkillMetrics.tsx +157 -124
  41. package/src/client/skill-hub/index.tsx +58 -51
  42. package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +132 -99
  43. package/src/client/skill-hub/tools/registerSkillLoopCards.ts +71 -58
  44. package/src/client/tools/registerOrchestratorCards.ts +17 -7
  45. package/src/client-v2/components/AIEmployeeSelect.tsx +47 -0
  46. package/src/client-v2/components/AIEmployeesContext.tsx +110 -0
  47. package/src/client-v2/components/AgentRunsTab.tsx +767 -0
  48. package/src/client-v2/components/HarnessProfilesTab.tsx +254 -0
  49. package/src/client-v2/components/RulesTab.tsx +782 -0
  50. package/src/client-v2/components/TracingTab.tsx +432 -0
  51. package/src/client-v2/hooks/useApiRequest.ts +114 -0
  52. package/src/client-v2/pages/AgentRunsPage.tsx +13 -0
  53. package/src/client-v2/pages/ExecutionHistoryPage.tsx +10 -0
  54. package/src/client-v2/pages/HarnessProfilesPage.tsx +10 -0
  55. package/src/client-v2/pages/LoopSettingsPage.tsx +10 -0
  56. package/src/client-v2/pages/RulesPage.tsx +13 -0
  57. package/src/client-v2/pages/SkillDefinitionsPage.tsx +10 -0
  58. package/src/client-v2/pages/SkillMetricsPage.tsx +10 -0
  59. package/src/client-v2/pages/TracingPage.tsx +13 -0
  60. package/src/client-v2/plugin.tsx +70 -0
  61. package/src/client-v2/skill-hub/components/ExecutionHistory.tsx +196 -0
  62. package/src/client-v2/skill-hub/components/FileLinkList.tsx +37 -0
  63. package/src/client-v2/skill-hub/components/GitSkillImport.tsx +539 -0
  64. package/src/client-v2/skill-hub/components/LoopSettings.tsx +331 -0
  65. package/src/client-v2/skill-hub/components/SkillEditor.tsx +453 -0
  66. package/src/client-v2/skill-hub/components/SkillManager.tsx +174 -0
  67. package/src/client-v2/skill-hub/components/SkillMetrics.tsx +157 -0
  68. package/src/client-v2/skill-hub/components/SkillTestPanel.tsx +135 -0
  69. package/src/client-v2/skill-hub/locale.ts +13 -0
  70. package/src/client-v2/skill-hub/tools/loopTemplates.ts +52 -0
  71. package/src/client-v2/skill-hub/utils/jsonFields.ts +41 -0
  72. package/src/client-v2/utils/jsonFields.ts +41 -0
  73. package/src/locale/en-US.json +7 -0
  74. package/src/locale/vi-VN.json +7 -0
  75. package/src/locale/zh-CN.json +27 -0
  76. package/src/server/__tests__/agent-registry-service.test.ts +147 -0
  77. package/src/server/__tests__/code-validator.test.ts +63 -0
  78. package/src/server/__tests__/skill-execute.test.ts +33 -0
  79. package/src/server/__tests__/skill-settings.test.ts +63 -0
  80. package/src/server/migrations/20260615000000-normalize-ai-employee-tool-bindings.ts +39 -0
  81. package/src/server/plugin.ts +68 -12
  82. package/src/server/services/AgentHarness.ts +49 -22
  83. package/src/server/services/AgentLoopController.ts +17 -6
  84. package/src/server/services/AgentLoopService.ts +1 -1
  85. package/src/server/services/AgentPlannerService.ts +10 -0
  86. package/src/server/services/AgentRegistryService.ts +89 -47
  87. package/src/server/services/CircuitBreaker.ts +10 -0
  88. package/src/server/services/CodeValidator.ts +237 -159
  89. package/src/server/services/SandboxRunner.ts +203 -189
  90. package/src/server/skill-hub/plugin.ts +933 -898
  91. package/src/server/tools/delegate-task.ts +12 -9
  92. package/src/server/tools/skill-execute.ts +194 -160
  93. package/src/server/utils/ai-manager.ts +24 -0
  94. package/src/server/utils/ctx-utils.ts +14 -0
  95. package/src/server/utils/skill-settings.ts +116 -0
  96. package/dist/client/AIEmployeeSelect.d.ts +0 -11
  97. package/dist/client/AIEmployeesContext.d.ts +0 -30
  98. package/dist/client/AgentRunsTab.d.ts +0 -2
  99. package/dist/client/HarnessProfilesTab.d.ts +0 -2
  100. package/dist/client/OrchestratorSettings.d.ts +0 -3
  101. package/dist/client/RulesTab.d.ts +0 -2
  102. package/dist/client/TracingTab.d.ts +0 -2
  103. package/dist/client/hooks/useRunEventStream.d.ts +0 -22
  104. package/dist/client/index.d.ts +0 -2
  105. package/dist/client/plugin.d.ts +0 -6
  106. package/dist/client/skill-hub/components/ExecutionHistory.d.ts +0 -2
  107. package/dist/client/skill-hub/components/ExecutionProgress.d.ts +0 -20
  108. package/dist/client/skill-hub/components/GitSkillImport.d.ts +0 -7
  109. package/dist/client/skill-hub/components/LoopSettings.d.ts +0 -2
  110. package/dist/client/skill-hub/components/SkillEditor.d.ts +0 -7
  111. package/dist/client/skill-hub/components/SkillManager.d.ts +0 -2
  112. package/dist/client/skill-hub/components/SkillMetrics.d.ts +0 -2
  113. package/dist/client/skill-hub/components/SkillTestPanel.d.ts +0 -7
  114. package/dist/client/skill-hub/index.d.ts +0 -11
  115. package/dist/client/skill-hub/locale.d.ts +0 -3
  116. package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +0 -6
  117. package/dist/client/skill-hub/tools/SkillHubCard.d.ts +0 -3
  118. package/dist/client/skill-hub/tools/loopTemplates.d.ts +0 -22
  119. package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +0 -1
  120. package/dist/client/skill-hub/utils/jsonFields.d.ts +0 -3
  121. package/dist/client/tools/PlanApprovalCard.d.ts +0 -3
  122. package/dist/client/tools/registerOrchestratorCards.d.ts +0 -1
  123. package/dist/index.d.ts +0 -2
  124. package/dist/server/collections/agent-execution-spans.d.ts +0 -9
  125. package/dist/server/collections/agent-harness-profiles.d.ts +0 -2
  126. package/dist/server/collections/agent-loop-events.d.ts +0 -2
  127. package/dist/server/collections/agent-loop-runs.d.ts +0 -2
  128. package/dist/server/collections/agent-loop-steps.d.ts +0 -2
  129. package/dist/server/collections/orchestrator-config.d.ts +0 -2
  130. package/dist/server/collections/orchestrator-logs.d.ts +0 -8
  131. package/dist/server/collections/skill-definitions.d.ts +0 -3
  132. package/dist/server/collections/skill-executions.d.ts +0 -3
  133. package/dist/server/collections/skill-loop-configs.d.ts +0 -3
  134. package/dist/server/collections/skill-worker-configs.d.ts +0 -3
  135. package/dist/server/migrations/20260423000000-add-progress-fields.d.ts +0 -4
  136. package/dist/server/migrations/20260425000000-add-interaction-schema.d.ts +0 -4
  137. package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +0 -7
  138. package/dist/server/migrations/20260427000000-change-packages-to-text.d.ts +0 -4
  139. package/dist/server/migrations/20260427000001-change-other-json-to-text.d.ts +0 -4
  140. package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +0 -7
  141. package/dist/server/migrations/20260429000000-fix-inputargs-json-to-text.d.ts +0 -16
  142. package/dist/server/migrations/20260503000000-add-orchestrator-trace-fields.d.ts +0 -7
  143. package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +0 -7
  144. package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +0 -12
  145. package/dist/server/migrations/20260601000000-add-token-fields.d.ts +0 -7
  146. package/dist/server/plugin.d.ts +0 -16
  147. package/dist/server/resources/agent-loop.d.ts +0 -3
  148. package/dist/server/resources/tracing.d.ts +0 -7
  149. package/dist/server/services/AgentHarness.d.ts +0 -44
  150. package/dist/server/services/AgentLoopController.d.ts +0 -218
  151. package/dist/server/services/AgentLoopRepository.d.ts +0 -20
  152. package/dist/server/services/AgentLoopService.d.ts +0 -159
  153. package/dist/server/services/AgentPlanValidator.d.ts +0 -4
  154. package/dist/server/services/AgentPlannerService.d.ts +0 -8
  155. package/dist/server/services/AgentRegistryService.d.ts +0 -21
  156. package/dist/server/services/CircuitBreaker.d.ts +0 -40
  157. package/dist/server/services/CodeValidator.d.ts +0 -32
  158. package/dist/server/services/ContextAggregator.d.ts +0 -45
  159. package/dist/server/services/ExecutionSpanService.d.ts +0 -46
  160. package/dist/server/services/FileManager.d.ts +0 -28
  161. package/dist/server/services/RunEventBus.d.ts +0 -9
  162. package/dist/server/services/SandboxRunner.d.ts +0 -41
  163. package/dist/server/services/SkillManager.d.ts +0 -6
  164. package/dist/server/services/SkillRepositoryService.d.ts +0 -22
  165. package/dist/server/services/TokenTracker.d.ts +0 -62
  166. package/dist/server/services/WorkerEnvManager.d.ts +0 -26
  167. package/dist/server/skill-hub/actions/git-import.d.ts +0 -21
  168. package/dist/server/skill-hub/mcp/McpController.d.ts +0 -15
  169. package/dist/server/skill-hub/plugin.d.ts +0 -61
  170. package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +0 -16
  171. package/dist/server/skill-hub/utils/json-fields.d.ts +0 -7
  172. package/dist/server/tools/agent-loop.d.ts +0 -235
  173. package/dist/server/tools/delegate-task.d.ts +0 -19
  174. package/dist/server/tools/external-rag-search.d.ts +0 -42
  175. package/dist/server/tools/orchestrator-plan.d.ts +0 -205
  176. package/dist/server/tools/skill-execute.d.ts +0 -36
  177. package/dist/server/types.d.ts +0 -47
  178. package/dist/server/utils/ctx-utils.d.ts +0 -30
  179. package/dist/server/utils/logging.d.ts +0 -6
  180. /package/{dist/server/index.d.ts → src/client-v2/index.tsx} +0 -0
@@ -0,0 +1,70 @@
1
+ import { Plugin, Application } from '@nocobase/client-v2';
2
+
3
+ export class PluginAgentOrchestratorClient extends Plugin<Record<string, never>, Application> {
4
+ async load() {
5
+ this.pluginSettingsManager.addMenuItem({
6
+ key: 'ai.orchestrator',
7
+ title: this.t('Agent Orchestrator'),
8
+ icon: 'ApartmentOutlined',
9
+ });
10
+
11
+ this.pluginSettingsManager.addPageTabItem({
12
+ menuKey: 'ai.orchestrator',
13
+ key: 'index',
14
+ title: this.t('Orchestration Rules'),
15
+ componentLoader: () => import('./pages/RulesPage'),
16
+ sort: -1,
17
+ });
18
+
19
+ this.pluginSettingsManager.addPageTabItem({
20
+ menuKey: 'ai.orchestrator',
21
+ key: 'tracing',
22
+ title: this.t('Execution Tracing'),
23
+ componentLoader: () => import('./pages/TracingPage'),
24
+ });
25
+
26
+ this.pluginSettingsManager.addPageTabItem({
27
+ menuKey: 'ai.orchestrator',
28
+ key: 'agent-runs',
29
+ title: this.t('Agent Runs'),
30
+ componentLoader: () => import('./pages/AgentRunsPage'),
31
+ });
32
+
33
+ this.pluginSettingsManager.addPageTabItem({
34
+ menuKey: 'ai.orchestrator',
35
+ key: 'harness-profiles',
36
+ title: this.t('Harness Profiles'),
37
+ componentLoader: () => import('./pages/HarnessProfilesPage'),
38
+ });
39
+
40
+ this.pluginSettingsManager.addPageTabItem({
41
+ menuKey: 'ai.orchestrator',
42
+ key: 'skill-definitions',
43
+ title: this.t('Skill Hub Definitions'),
44
+ componentLoader: () => import('./pages/SkillDefinitionsPage'),
45
+ });
46
+
47
+ this.pluginSettingsManager.addPageTabItem({
48
+ menuKey: 'ai.orchestrator',
49
+ key: 'skill-executions',
50
+ title: this.t('Execution History'),
51
+ componentLoader: () => import('./pages/ExecutionHistoryPage'),
52
+ });
53
+
54
+ this.pluginSettingsManager.addPageTabItem({
55
+ menuKey: 'ai.orchestrator',
56
+ key: 'skill-loop-settings',
57
+ title: this.t('Skill Review Settings'),
58
+ componentLoader: () => import('./pages/LoopSettingsPage'),
59
+ });
60
+
61
+ this.pluginSettingsManager.addPageTabItem({
62
+ menuKey: 'ai.orchestrator',
63
+ key: 'skill-metrics',
64
+ title: this.t('Metrics'),
65
+ componentLoader: () => import('./pages/SkillMetricsPage'),
66
+ });
67
+ }
68
+ }
69
+
70
+ export default PluginAgentOrchestratorClient;
@@ -0,0 +1,196 @@
1
+ import React, { useState, useEffect, useCallback } from 'react';
2
+ import { Card, Table, Tag, Button, Typography, Space, Tooltip, Popconfirm, message } from 'antd';
3
+ import { ReloadOutlined, DeleteOutlined } from '@ant-design/icons';
4
+ import { useApiClient as useAPIClient } from '../../hooks/useApiRequest';
5
+ import { useT } from '../locale';
6
+ import { parseJsonText } from '../utils/jsonFields';
7
+ import { FileLinkList } from './FileLinkList';
8
+
9
+ const STATUS_COLORS: Record<string, string> = {
10
+ pending: 'default',
11
+ running: 'processing',
12
+ succeeded: 'success',
13
+ failed: 'error',
14
+ canceled: 'warning',
15
+ timeout: 'error',
16
+ };
17
+
18
+ export const ExecutionHistory: React.FC = () => {
19
+ const api = useAPIClient();
20
+ const t = useT();
21
+ const [executions, setExecutions] = useState<any[]>([]);
22
+ const [loading, setLoading] = useState(false);
23
+ const [page, setPage] = useState(1);
24
+ const [total, setTotal] = useState(0);
25
+ const pageSize = 20;
26
+
27
+ const fetchExecutions = useCallback(async () => {
28
+ setLoading(true);
29
+ try {
30
+ const { data } = await api.request({
31
+ url: 'skillExecutions:list',
32
+ params: {
33
+ page,
34
+ pageSize,
35
+ sort: ['-createdAt'],
36
+ appends: ['skill', 'triggeredBy'],
37
+ },
38
+ });
39
+ const rawData = data?.data?.data ?? data?.data ?? [];
40
+ setExecutions(Array.isArray(rawData) ? rawData : []);
41
+ setTotal(data?.meta?.count || 0);
42
+ } catch {
43
+ // ignore
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ }, [api, page]);
48
+
49
+ useEffect(() => {
50
+ fetchExecutions();
51
+ }, [fetchExecutions]);
52
+
53
+ const handleDelete = async (id: number) => {
54
+ try {
55
+ await api.request({
56
+ url: `skillExecutions:destroy`,
57
+ method: 'POST',
58
+ params: {
59
+ filterByTk: id,
60
+ },
61
+ });
62
+ message.success(t('Deleted successfully'));
63
+ fetchExecutions();
64
+ } catch (err: any) {
65
+ message.error(err?.response?.data?.errors?.[0]?.message || t('Delete failed'));
66
+ }
67
+ };
68
+
69
+ const columns = [
70
+ {
71
+ title: 'ID',
72
+ dataIndex: 'id',
73
+ key: 'id',
74
+ width: 80,
75
+ },
76
+ {
77
+ title: t('Skill'),
78
+ dataIndex: ['skill', 'title'],
79
+ key: 'skill',
80
+ width: 180,
81
+ },
82
+ {
83
+ title: t('Status'),
84
+ dataIndex: 'status',
85
+ key: 'status',
86
+ width: 120,
87
+ render: (status: string) => <Tag color={STATUS_COLORS[status] || 'default'}>{status}</Tag>,
88
+ },
89
+ {
90
+ title: t('Duration'),
91
+ dataIndex: 'durationMs',
92
+ key: 'duration',
93
+ width: 100,
94
+ render: (ms: number) => (ms ? `${(ms / 1000).toFixed(1)}s` : '-'),
95
+ },
96
+ {
97
+ title: t('Files'),
98
+ dataIndex: 'outputFiles',
99
+ key: 'files',
100
+ width: 250,
101
+ render: (files: any[], record: any) => {
102
+ const parsed = parseJsonText<any[]>(files, []);
103
+ if (!Array.isArray(parsed) || !parsed.length) return '-';
104
+ const formattedFiles = parsed.map((f) => ({
105
+ name: f.name,
106
+ url: `/api/skillHub:download?execId=${record.id}&filename=${encodeURIComponent(f.name)}`,
107
+ }));
108
+ return <FileLinkList files={formattedFiles} />;
109
+ },
110
+ },
111
+ {
112
+ title: t('Triggered By'),
113
+ dataIndex: ['triggeredBy', 'nickname'],
114
+ key: 'triggeredBy',
115
+ width: 120,
116
+ render: (v: string) => v || '-',
117
+ },
118
+ {
119
+ title: t('Created At'),
120
+ dataIndex: 'createdAt',
121
+ key: 'createdAt',
122
+ width: 180,
123
+ render: (v: string) => (v ? new Date(v).toLocaleString() : '-'),
124
+ },
125
+ {
126
+ title: t('Output'),
127
+ key: 'output',
128
+ width: 200,
129
+ render: (_: any, record: any) => (
130
+ <Space direction="vertical" size={0}>
131
+ {record.stdout && (
132
+ <Tooltip title={record.stdout}>
133
+ <Typography.Text style={{ fontSize: 12, maxWidth: 200 }} ellipsis>
134
+ {record.stdout}
135
+ </Typography.Text>
136
+ </Tooltip>
137
+ )}
138
+ {record.stderr && (
139
+ <Tooltip title={record.stderr}>
140
+ <Typography.Text type="danger" style={{ fontSize: 12, maxWidth: 200 }} ellipsis>
141
+ {record.stderr}
142
+ </Typography.Text>
143
+ </Tooltip>
144
+ )}
145
+ </Space>
146
+ ),
147
+ },
148
+ {
149
+ title: t('Actions'),
150
+ key: 'actions',
151
+ width: 80,
152
+ fixed: 'right' as const,
153
+ render: (_: any, record: any) => (
154
+ <Space size="middle">
155
+ <Popconfirm
156
+ title={t('Are you sure to delete this execution history and its files?')}
157
+ onConfirm={() => handleDelete(record.id)}
158
+ okText={t('Yes')}
159
+ cancelText={t('No')}
160
+ >
161
+ <Button type="text" danger icon={<DeleteOutlined />} size="small" />
162
+ </Popconfirm>
163
+ </Space>
164
+ ),
165
+ },
166
+ ];
167
+
168
+ return (
169
+ <Card
170
+ title={t('Execution History')}
171
+ extra={
172
+ <Space>
173
+ <Button icon={<ReloadOutlined />} onClick={fetchExecutions} loading={loading}>
174
+ {t('Refresh')}
175
+ </Button>
176
+ </Space>
177
+ }
178
+ >
179
+ <Table
180
+ dataSource={executions}
181
+ columns={columns}
182
+ rowKey="id"
183
+ loading={loading}
184
+ size="middle"
185
+ pagination={{
186
+ current: page,
187
+ pageSize,
188
+ total,
189
+ onChange: setPage,
190
+ showTotal: (count) => `Total: ${count}`,
191
+ }}
192
+ scroll={{ x: 1200 }}
193
+ />
194
+ </Card>
195
+ );
196
+ };
@@ -0,0 +1,37 @@
1
+ import React from 'react';
2
+ import { Space, Typography } from 'antd';
3
+ import { PaperClipOutlined } from '@ant-design/icons';
4
+
5
+ export interface FileLinkItem {
6
+ name: string;
7
+ url?: string;
8
+ downloadUrl?: string;
9
+ }
10
+
11
+ /**
12
+ * client-v2 replacement for the v1 `Upload.ReadPretty` file list, which is not
13
+ * exported by `@nocobase/client-v2`. Renders a simple vertical list of download
14
+ * links; non-resolvable entries fall back to plain text.
15
+ */
16
+ export const FileLinkList: React.FC<{ files: FileLinkItem[] }> = ({ files }) => {
17
+ if (!Array.isArray(files) || files.length === 0) {
18
+ return <Typography.Text type="secondary">-</Typography.Text>;
19
+ }
20
+ return (
21
+ <Space direction="vertical" size={2} style={{ width: '100%' }}>
22
+ {files.map((file, index) => {
23
+ const url = file.downloadUrl || file.url;
24
+ const label = file.name || `file-${index + 1}`;
25
+ return url ? (
26
+ <a key={`${label}-${index}`} href={url} target="_blank" rel="noreferrer">
27
+ <PaperClipOutlined /> {label}
28
+ </a>
29
+ ) : (
30
+ <Typography.Text key={`${label}-${index}`}>
31
+ <PaperClipOutlined /> {label}
32
+ </Typography.Text>
33
+ );
34
+ })}
35
+ </Space>
36
+ );
37
+ };