makecc 0.2.5 → 0.2.7
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/client/assets/{index-c7WYmobg.js → index-CclsqEDr.js} +42 -42
- package/dist/client/index.html +1 -1
- package/dist/server/index.js +81 -0
- package/dist/server/services/nodeSyncService.js +313 -0
- package/package.json +1 -1
- package/server/index.ts +81 -0
- package/server/services/nodeSyncService.ts +384 -0
package/dist/client/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>vite-temp</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-CclsqEDr.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-DLT8bQFx.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/dist/server/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import { ClaudeService } from './services/claudeService';
|
|
|
10
10
|
import { fileService } from './services/fileService';
|
|
11
11
|
import { workflowAIService } from './services/workflowAIService';
|
|
12
12
|
import { skillGeneratorService } from './services/skillGeneratorService';
|
|
13
|
+
import { nodeSyncService } from './services/nodeSyncService';
|
|
13
14
|
import { workflowExecutionService } from './services/workflowExecutionService';
|
|
14
15
|
import { executeInTerminal } from './services/terminalService';
|
|
15
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -90,6 +91,86 @@ app.get('/api/settings/api-key', async (req, res) => {
|
|
|
90
91
|
res.status(500).json({ message: errorMessage });
|
|
91
92
|
}
|
|
92
93
|
});
|
|
94
|
+
// Sync node to file system
|
|
95
|
+
app.post('/api/sync/node', async (req, res) => {
|
|
96
|
+
try {
|
|
97
|
+
const { node } = req.body;
|
|
98
|
+
if (!node) {
|
|
99
|
+
return res.status(400).json({ message: 'Node data is required' });
|
|
100
|
+
}
|
|
101
|
+
const result = await nodeSyncService.syncNode(node);
|
|
102
|
+
if (result.success) {
|
|
103
|
+
res.json(result);
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
res.status(500).json({ message: result.error });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
111
|
+
res.status(500).json({ message: errorMessage });
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
// Delete node from file system
|
|
115
|
+
app.delete('/api/sync/node', async (req, res) => {
|
|
116
|
+
try {
|
|
117
|
+
const { node } = req.body;
|
|
118
|
+
if (!node) {
|
|
119
|
+
return res.status(400).json({ message: 'Node data is required' });
|
|
120
|
+
}
|
|
121
|
+
const result = await nodeSyncService.deleteNode(node);
|
|
122
|
+
if (result.success) {
|
|
123
|
+
res.json(result);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
res.status(500).json({ message: result.error });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
131
|
+
res.status(500).json({ message: errorMessage });
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
// Sync edge (connection) to file system
|
|
135
|
+
app.post('/api/sync/edge', async (req, res) => {
|
|
136
|
+
try {
|
|
137
|
+
const { edge, nodes } = req.body;
|
|
138
|
+
if (!edge || !nodes) {
|
|
139
|
+
return res.status(400).json({ message: 'Edge and nodes data are required' });
|
|
140
|
+
}
|
|
141
|
+
const result = await nodeSyncService.syncEdge(edge, nodes);
|
|
142
|
+
if (result.success) {
|
|
143
|
+
res.json(result);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
res.status(500).json({ message: result.error });
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
151
|
+
res.status(500).json({ message: errorMessage });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
// Remove edge from file system
|
|
155
|
+
app.delete('/api/sync/edge', async (req, res) => {
|
|
156
|
+
try {
|
|
157
|
+
const { edge, nodes } = req.body;
|
|
158
|
+
if (!edge || !nodes) {
|
|
159
|
+
return res.status(400).json({ message: 'Edge and nodes data are required' });
|
|
160
|
+
}
|
|
161
|
+
const result = await nodeSyncService.removeEdge(edge, nodes);
|
|
162
|
+
if (result.success) {
|
|
163
|
+
res.json(result);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
res.status(500).json({ message: result.error });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
171
|
+
res.status(500).json({ message: errorMessage });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
93
174
|
// Generate skill using AI
|
|
94
175
|
app.post('/api/generate/skill', async (req, res) => {
|
|
95
176
|
try {
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
export class NodeSyncService {
|
|
4
|
+
projectRoot;
|
|
5
|
+
constructor(projectRoot) {
|
|
6
|
+
this.projectRoot = projectRoot || process.env.MAKECC_PROJECT_PATH || process.cwd();
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 노드 생성/수정 시 파일 동기화
|
|
10
|
+
*/
|
|
11
|
+
async syncNode(node) {
|
|
12
|
+
try {
|
|
13
|
+
switch (node.type) {
|
|
14
|
+
case 'skill':
|
|
15
|
+
return await this.syncSkillNode(node);
|
|
16
|
+
case 'subagent':
|
|
17
|
+
return await this.syncSubagentNode(node);
|
|
18
|
+
case 'command':
|
|
19
|
+
return await this.syncCommandNode(node);
|
|
20
|
+
case 'hook':
|
|
21
|
+
return await this.syncHookNode(node);
|
|
22
|
+
case 'input':
|
|
23
|
+
case 'output':
|
|
24
|
+
// 입력/출력 노드는 파일 저장 불필요
|
|
25
|
+
return { success: true };
|
|
26
|
+
default:
|
|
27
|
+
return { success: false, error: `Unknown node type: ${node.type}` };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
32
|
+
return { success: false, error: errorMessage };
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 노드 삭제 시 파일 삭제
|
|
37
|
+
*/
|
|
38
|
+
async deleteNode(node) {
|
|
39
|
+
try {
|
|
40
|
+
switch (node.type) {
|
|
41
|
+
case 'skill':
|
|
42
|
+
if (node.skillId) {
|
|
43
|
+
const skillPath = path.join(this.projectRoot, '.claude', 'skills', node.skillId);
|
|
44
|
+
await fs.rm(skillPath, { recursive: true, force: true });
|
|
45
|
+
}
|
|
46
|
+
break;
|
|
47
|
+
case 'subagent':
|
|
48
|
+
const agentName = this.toKebabCase(node.label);
|
|
49
|
+
const agentPath = path.join(this.projectRoot, '.claude', 'agents', `${agentName}.md`);
|
|
50
|
+
await fs.unlink(agentPath).catch(() => { });
|
|
51
|
+
break;
|
|
52
|
+
case 'command':
|
|
53
|
+
const cmdName = node.commandName || this.toKebabCase(node.label);
|
|
54
|
+
const cmdPath = path.join(this.projectRoot, '.claude', 'commands', `${cmdName}.md`);
|
|
55
|
+
await fs.unlink(cmdPath).catch(() => { });
|
|
56
|
+
break;
|
|
57
|
+
case 'hook':
|
|
58
|
+
await this.removeHookFromSettings(node);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
return { success: true };
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
65
|
+
return { success: false, error: errorMessage };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* 엣지 연결 시 관계 업데이트
|
|
70
|
+
*/
|
|
71
|
+
async syncEdge(edge, nodes) {
|
|
72
|
+
try {
|
|
73
|
+
const sourceNode = nodes.find(n => n.id === edge.source);
|
|
74
|
+
const targetNode = nodes.find(n => n.id === edge.target);
|
|
75
|
+
if (!sourceNode || !targetNode) {
|
|
76
|
+
return { success: false, error: 'Source or target node not found' };
|
|
77
|
+
}
|
|
78
|
+
// 서브에이전트 → 스킬 연결: 서브에이전트의 skills 필드 업데이트
|
|
79
|
+
if (sourceNode.type === 'subagent' && targetNode.type === 'skill') {
|
|
80
|
+
const skills = sourceNode.skills || [];
|
|
81
|
+
const skillId = targetNode.skillId || this.toKebabCase(targetNode.label);
|
|
82
|
+
if (!skills.includes(skillId)) {
|
|
83
|
+
skills.push(skillId);
|
|
84
|
+
sourceNode.skills = skills;
|
|
85
|
+
await this.syncSubagentNode(sourceNode);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
// 스킬 → 서브에이전트 연결: 서브에이전트의 skills 필드 업데이트
|
|
89
|
+
if (sourceNode.type === 'skill' && targetNode.type === 'subagent') {
|
|
90
|
+
const skills = targetNode.skills || [];
|
|
91
|
+
const skillId = sourceNode.skillId || this.toKebabCase(sourceNode.label);
|
|
92
|
+
if (!skills.includes(skillId)) {
|
|
93
|
+
skills.push(skillId);
|
|
94
|
+
targetNode.skills = skills;
|
|
95
|
+
await this.syncSubagentNode(targetNode);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return { success: true };
|
|
99
|
+
}
|
|
100
|
+
catch (error) {
|
|
101
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
102
|
+
return { success: false, error: errorMessage };
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 엣지 삭제 시 관계 업데이트
|
|
107
|
+
*/
|
|
108
|
+
async removeEdge(edge, nodes) {
|
|
109
|
+
try {
|
|
110
|
+
const sourceNode = nodes.find(n => n.id === edge.source);
|
|
111
|
+
const targetNode = nodes.find(n => n.id === edge.target);
|
|
112
|
+
if (!sourceNode || !targetNode) {
|
|
113
|
+
return { success: true }; // 노드가 없으면 무시
|
|
114
|
+
}
|
|
115
|
+
// 서브에이전트에서 스킬 제거
|
|
116
|
+
if (sourceNode.type === 'subagent' && targetNode.type === 'skill') {
|
|
117
|
+
const skillId = targetNode.skillId || this.toKebabCase(targetNode.label);
|
|
118
|
+
sourceNode.skills = (sourceNode.skills || []).filter(s => s !== skillId);
|
|
119
|
+
await this.syncSubagentNode(sourceNode);
|
|
120
|
+
}
|
|
121
|
+
if (sourceNode.type === 'skill' && targetNode.type === 'subagent') {
|
|
122
|
+
const skillId = sourceNode.skillId || this.toKebabCase(sourceNode.label);
|
|
123
|
+
targetNode.skills = (targetNode.skills || []).filter(s => s !== skillId);
|
|
124
|
+
await this.syncSubagentNode(targetNode);
|
|
125
|
+
}
|
|
126
|
+
return { success: true };
|
|
127
|
+
}
|
|
128
|
+
catch (error) {
|
|
129
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
130
|
+
return { success: false, error: errorMessage };
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// ===== Private Methods =====
|
|
134
|
+
async syncSkillNode(node) {
|
|
135
|
+
const skillId = node.skillId || this.toKebabCase(node.label);
|
|
136
|
+
const skillPath = path.join(this.projectRoot, '.claude', 'skills', skillId);
|
|
137
|
+
const skillMdPath = path.join(skillPath, 'SKILL.md');
|
|
138
|
+
await fs.mkdir(skillPath, { recursive: true });
|
|
139
|
+
// 기존 파일이 있으면 읽어서 업데이트, 없으면 새로 생성
|
|
140
|
+
let content = '';
|
|
141
|
+
try {
|
|
142
|
+
content = await fs.readFile(skillMdPath, 'utf-8');
|
|
143
|
+
// frontmatter 업데이트
|
|
144
|
+
content = this.updateFrontmatter(content, {
|
|
145
|
+
name: skillId,
|
|
146
|
+
description: node.description || node.label,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// 새로 생성
|
|
151
|
+
content = `---
|
|
152
|
+
name: ${skillId}
|
|
153
|
+
description: ${node.description || node.label}
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
# ${node.label}
|
|
157
|
+
|
|
158
|
+
## 사용 시점
|
|
159
|
+
이 스킬은 다음 상황에서 사용됩니다:
|
|
160
|
+
- ${node.description || '설명을 추가하세요'}
|
|
161
|
+
|
|
162
|
+
## 사용 방법
|
|
163
|
+
|
|
164
|
+
\`\`\`bash
|
|
165
|
+
# 스킬 사용 방법을 여기에 작성하세요
|
|
166
|
+
\`\`\`
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
await fs.writeFile(skillMdPath, content, 'utf-8');
|
|
170
|
+
return { success: true, path: skillPath };
|
|
171
|
+
}
|
|
172
|
+
async syncSubagentNode(node) {
|
|
173
|
+
const agentName = this.toKebabCase(node.label);
|
|
174
|
+
const agentsDir = path.join(this.projectRoot, '.claude', 'agents');
|
|
175
|
+
const agentPath = path.join(agentsDir, `${agentName}.md`);
|
|
176
|
+
await fs.mkdir(agentsDir, { recursive: true });
|
|
177
|
+
// Frontmatter 구성
|
|
178
|
+
const frontmatter = {
|
|
179
|
+
name: agentName,
|
|
180
|
+
description: node.description || node.label,
|
|
181
|
+
};
|
|
182
|
+
if (node.tools && node.tools.length > 0) {
|
|
183
|
+
frontmatter.tools = node.tools.join(', ');
|
|
184
|
+
}
|
|
185
|
+
if (node.model) {
|
|
186
|
+
frontmatter.model = node.model;
|
|
187
|
+
}
|
|
188
|
+
if (node.skills && node.skills.length > 0) {
|
|
189
|
+
frontmatter.skills = node.skills.join(', ');
|
|
190
|
+
}
|
|
191
|
+
const frontmatterStr = Object.entries(frontmatter)
|
|
192
|
+
.map(([key, value]) => `${key}: ${value}`)
|
|
193
|
+
.join('\n');
|
|
194
|
+
const content = `---
|
|
195
|
+
${frontmatterStr}
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
${node.systemPrompt || `You are ${node.label}.
|
|
199
|
+
|
|
200
|
+
${node.description || ''}
|
|
201
|
+
`}
|
|
202
|
+
`;
|
|
203
|
+
await fs.writeFile(agentPath, content, 'utf-8');
|
|
204
|
+
return { success: true, path: agentPath };
|
|
205
|
+
}
|
|
206
|
+
async syncCommandNode(node) {
|
|
207
|
+
const cmdName = node.commandName || this.toKebabCase(node.label);
|
|
208
|
+
const commandsDir = path.join(this.projectRoot, '.claude', 'commands');
|
|
209
|
+
const cmdPath = path.join(commandsDir, `${cmdName}.md`);
|
|
210
|
+
await fs.mkdir(commandsDir, { recursive: true });
|
|
211
|
+
const content = node.commandContent || `---
|
|
212
|
+
description: ${node.description || node.label}
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
${node.description || '커맨드 내용을 여기에 작성하세요'}
|
|
216
|
+
|
|
217
|
+
$ARGUMENTS
|
|
218
|
+
`;
|
|
219
|
+
await fs.writeFile(cmdPath, content, 'utf-8');
|
|
220
|
+
return { success: true, path: cmdPath };
|
|
221
|
+
}
|
|
222
|
+
async syncHookNode(node) {
|
|
223
|
+
const settingsPath = path.join(this.projectRoot, '.claude', 'settings.json');
|
|
224
|
+
// 기존 settings 읽기
|
|
225
|
+
let settings = {};
|
|
226
|
+
try {
|
|
227
|
+
const content = await fs.readFile(settingsPath, 'utf-8');
|
|
228
|
+
settings = JSON.parse(content);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
// 파일 없음
|
|
232
|
+
}
|
|
233
|
+
// hooks 섹션 확인/생성
|
|
234
|
+
if (!settings.hooks) {
|
|
235
|
+
settings.hooks = {};
|
|
236
|
+
}
|
|
237
|
+
const hooks = settings.hooks;
|
|
238
|
+
const event = node.hookEvent || 'PreToolUse';
|
|
239
|
+
if (!hooks[event]) {
|
|
240
|
+
hooks[event] = [];
|
|
241
|
+
}
|
|
242
|
+
// 기존 훅 찾기 (같은 matcher로)
|
|
243
|
+
const eventHooks = hooks[event];
|
|
244
|
+
const existingIndex = eventHooks.findIndex(h => h.matcher === (node.hookMatcher || '*'));
|
|
245
|
+
const hookConfig = {
|
|
246
|
+
matcher: node.hookMatcher || '*',
|
|
247
|
+
hooks: [
|
|
248
|
+
{
|
|
249
|
+
type: 'command',
|
|
250
|
+
command: node.hookCommand || 'echo "Hook triggered"',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
};
|
|
254
|
+
if (existingIndex >= 0) {
|
|
255
|
+
eventHooks[existingIndex] = hookConfig;
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
eventHooks.push(hookConfig);
|
|
259
|
+
}
|
|
260
|
+
// .claude 디렉토리 생성
|
|
261
|
+
await fs.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
262
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
263
|
+
return { success: true, path: settingsPath };
|
|
264
|
+
}
|
|
265
|
+
async removeHookFromSettings(node) {
|
|
266
|
+
const settingsPath = path.join(this.projectRoot, '.claude', 'settings.json');
|
|
267
|
+
let settings = {};
|
|
268
|
+
try {
|
|
269
|
+
const content = await fs.readFile(settingsPath, 'utf-8');
|
|
270
|
+
settings = JSON.parse(content);
|
|
271
|
+
}
|
|
272
|
+
catch {
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
const hooks = settings.hooks;
|
|
276
|
+
if (!hooks)
|
|
277
|
+
return;
|
|
278
|
+
const event = node.hookEvent || 'PreToolUse';
|
|
279
|
+
if (!hooks[event])
|
|
280
|
+
return;
|
|
281
|
+
const eventHooks = hooks[event];
|
|
282
|
+
hooks[event] = eventHooks.filter(h => h.matcher !== (node.hookMatcher || '*'));
|
|
283
|
+
await fs.writeFile(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
|
|
284
|
+
}
|
|
285
|
+
updateFrontmatter(content, updates) {
|
|
286
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
287
|
+
if (!frontmatterMatch) {
|
|
288
|
+
// frontmatter 없으면 추가
|
|
289
|
+
const fm = Object.entries(updates)
|
|
290
|
+
.map(([k, v]) => `${k}: ${v}`)
|
|
291
|
+
.join('\n');
|
|
292
|
+
return `---\n${fm}\n---\n\n${content}`;
|
|
293
|
+
}
|
|
294
|
+
let frontmatter = frontmatterMatch[1];
|
|
295
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
296
|
+
const regex = new RegExp(`^${key}:.*$`, 'm');
|
|
297
|
+
if (regex.test(frontmatter)) {
|
|
298
|
+
frontmatter = frontmatter.replace(regex, `${key}: ${value}`);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
frontmatter += `\n${key}: ${value}`;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return content.replace(/^---\n[\s\S]*?\n---/, `---\n${frontmatter}\n---`);
|
|
305
|
+
}
|
|
306
|
+
toKebabCase(str) {
|
|
307
|
+
return str
|
|
308
|
+
.toLowerCase()
|
|
309
|
+
.replace(/[^a-z0-9가-힣]+/g, '-')
|
|
310
|
+
.replace(/^-|-$/g, '');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
export const nodeSyncService = new NodeSyncService();
|
package/package.json
CHANGED
package/server/index.ts
CHANGED
|
@@ -10,6 +10,7 @@ import { ClaudeService } from './services/claudeService';
|
|
|
10
10
|
import { fileService } from './services/fileService';
|
|
11
11
|
import { workflowAIService } from './services/workflowAIService';
|
|
12
12
|
import { skillGeneratorService } from './services/skillGeneratorService';
|
|
13
|
+
import { nodeSyncService } from './services/nodeSyncService';
|
|
13
14
|
import { workflowExecutionService } from './services/workflowExecutionService';
|
|
14
15
|
import { executeInTerminal, getClaudeCommand } from './services/terminalService';
|
|
15
16
|
import type { WorkflowExecutionRequest, NodeExecutionUpdate } from './types';
|
|
@@ -108,6 +109,86 @@ app.get('/api/settings/api-key', async (req, res) => {
|
|
|
108
109
|
}
|
|
109
110
|
});
|
|
110
111
|
|
|
112
|
+
// Sync node to file system
|
|
113
|
+
app.post('/api/sync/node', async (req, res) => {
|
|
114
|
+
try {
|
|
115
|
+
const { node } = req.body;
|
|
116
|
+
if (!node) {
|
|
117
|
+
return res.status(400).json({ message: 'Node data is required' });
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result = await nodeSyncService.syncNode(node);
|
|
121
|
+
if (result.success) {
|
|
122
|
+
res.json(result);
|
|
123
|
+
} else {
|
|
124
|
+
res.status(500).json({ message: result.error });
|
|
125
|
+
}
|
|
126
|
+
} catch (error) {
|
|
127
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
128
|
+
res.status(500).json({ message: errorMessage });
|
|
129
|
+
}
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
// Delete node from file system
|
|
133
|
+
app.delete('/api/sync/node', async (req, res) => {
|
|
134
|
+
try {
|
|
135
|
+
const { node } = req.body;
|
|
136
|
+
if (!node) {
|
|
137
|
+
return res.status(400).json({ message: 'Node data is required' });
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const result = await nodeSyncService.deleteNode(node);
|
|
141
|
+
if (result.success) {
|
|
142
|
+
res.json(result);
|
|
143
|
+
} else {
|
|
144
|
+
res.status(500).json({ message: result.error });
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
148
|
+
res.status(500).json({ message: errorMessage });
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// Sync edge (connection) to file system
|
|
153
|
+
app.post('/api/sync/edge', async (req, res) => {
|
|
154
|
+
try {
|
|
155
|
+
const { edge, nodes } = req.body;
|
|
156
|
+
if (!edge || !nodes) {
|
|
157
|
+
return res.status(400).json({ message: 'Edge and nodes data are required' });
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const result = await nodeSyncService.syncEdge(edge, nodes);
|
|
161
|
+
if (result.success) {
|
|
162
|
+
res.json(result);
|
|
163
|
+
} else {
|
|
164
|
+
res.status(500).json({ message: result.error });
|
|
165
|
+
}
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
168
|
+
res.status(500).json({ message: errorMessage });
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Remove edge from file system
|
|
173
|
+
app.delete('/api/sync/edge', async (req, res) => {
|
|
174
|
+
try {
|
|
175
|
+
const { edge, nodes } = req.body;
|
|
176
|
+
if (!edge || !nodes) {
|
|
177
|
+
return res.status(400).json({ message: 'Edge and nodes data are required' });
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const result = await nodeSyncService.removeEdge(edge, nodes);
|
|
181
|
+
if (result.success) {
|
|
182
|
+
res.json(result);
|
|
183
|
+
} else {
|
|
184
|
+
res.status(500).json({ message: result.error });
|
|
185
|
+
}
|
|
186
|
+
} catch (error) {
|
|
187
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
188
|
+
res.status(500).json({ message: errorMessage });
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
|
|
111
192
|
// Generate skill using AI
|
|
112
193
|
app.post('/api/generate/skill', async (req, res) => {
|
|
113
194
|
try {
|