@codemieai/cdk 0.1.439 → 0.1.445
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/cli/index.js +1818 -621
- package/dist/index.d.ts +68 -9
- package/dist/index.js +1959 -851
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -1,12 +1,878 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as path from 'path';
|
|
3
|
-
import * as yaml from 'yaml';
|
|
1
|
+
import ora from 'ora';
|
|
4
2
|
import * as crypto from 'crypto';
|
|
5
3
|
import { CodeMieClient, DataSourceType } from 'codemie-sdk';
|
|
4
|
+
import * as fs from 'fs';
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as yaml6 from 'yaml';
|
|
6
7
|
import 'dotenv/config';
|
|
7
8
|
import pLimit from 'p-limit';
|
|
9
|
+
import * as readline from 'readline';
|
|
10
|
+
|
|
11
|
+
var __defProp = Object.defineProperty;
|
|
12
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
13
|
+
var __esm = (fn, res) => function __init() {
|
|
14
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
15
|
+
};
|
|
16
|
+
var __export = (target, all) => {
|
|
17
|
+
for (var name in all)
|
|
18
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
19
|
+
};
|
|
20
|
+
function pauseSpinner(operation) {
|
|
21
|
+
return spinnerManager.pause(operation);
|
|
22
|
+
}
|
|
23
|
+
function pauseSpinnerWhile(operation) {
|
|
24
|
+
return spinnerManager.pauseWhile(operation);
|
|
25
|
+
}
|
|
26
|
+
var defaultSpinnerFactory, CommandSpinnerManager, spinnerManager;
|
|
27
|
+
var init_commandSpinner = __esm({
|
|
28
|
+
"src/lib/commandSpinner.ts"() {
|
|
29
|
+
defaultSpinnerFactory = (text) => ora({
|
|
30
|
+
text,
|
|
31
|
+
discardStdin: false
|
|
32
|
+
});
|
|
33
|
+
CommandSpinnerManager = class {
|
|
34
|
+
spinner = null;
|
|
35
|
+
spinnerText = "";
|
|
36
|
+
factory = defaultSpinnerFactory;
|
|
37
|
+
setFactory(factory) {
|
|
38
|
+
this.factory = factory;
|
|
39
|
+
}
|
|
40
|
+
resetFactory() {
|
|
41
|
+
this.factory = defaultSpinnerFactory;
|
|
42
|
+
this.reset();
|
|
43
|
+
}
|
|
44
|
+
reset() {
|
|
45
|
+
if (this.spinner?.isSpinning) {
|
|
46
|
+
this.spinner.stop();
|
|
47
|
+
}
|
|
48
|
+
this.spinner = null;
|
|
49
|
+
this.spinnerText = "";
|
|
50
|
+
}
|
|
51
|
+
isEnabled() {
|
|
52
|
+
return Boolean(process.stderr?.isTTY);
|
|
53
|
+
}
|
|
54
|
+
start(text) {
|
|
55
|
+
this.spinnerText = text;
|
|
56
|
+
if (!this.isEnabled()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (!this.spinner) {
|
|
60
|
+
this.spinner = this.factory(text);
|
|
61
|
+
this.spinner.start();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
this.spinner.text = text;
|
|
65
|
+
if (!this.spinner.isSpinning) {
|
|
66
|
+
this.spinner.start();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
stop() {
|
|
70
|
+
if (this.spinner?.isSpinning) {
|
|
71
|
+
this.spinner.stop();
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
succeed(text) {
|
|
75
|
+
if (!this.spinner) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
this.spinner.succeed(text ?? this.spinnerText);
|
|
79
|
+
this.spinner = null;
|
|
80
|
+
this.spinnerText = "";
|
|
81
|
+
}
|
|
82
|
+
fail(text) {
|
|
83
|
+
if (!this.spinner) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.spinner.fail(text ?? this.spinnerText);
|
|
87
|
+
this.spinner = null;
|
|
88
|
+
this.spinnerText = "";
|
|
89
|
+
}
|
|
90
|
+
pause(operation) {
|
|
91
|
+
const shouldResume = Boolean(this.spinner?.isSpinning);
|
|
92
|
+
const currentText = this.spinner?.text ?? this.spinnerText;
|
|
93
|
+
if (shouldResume) {
|
|
94
|
+
this.spinner?.stop();
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
return operation();
|
|
98
|
+
} finally {
|
|
99
|
+
if (shouldResume && this.spinner) {
|
|
100
|
+
this.spinner.text = currentText;
|
|
101
|
+
this.spinner.start();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async pauseWhile(operation) {
|
|
106
|
+
const shouldResume = Boolean(this.spinner?.isSpinning);
|
|
107
|
+
const currentText = this.spinner?.text ?? this.spinnerText;
|
|
108
|
+
if (shouldResume) {
|
|
109
|
+
this.spinner?.stop();
|
|
110
|
+
}
|
|
111
|
+
try {
|
|
112
|
+
return await operation();
|
|
113
|
+
} finally {
|
|
114
|
+
if (shouldResume && this.spinner) {
|
|
115
|
+
this.spinner.text = currentText;
|
|
116
|
+
this.spinner.start();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
spinnerManager = new CommandSpinnerManager();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
8
124
|
|
|
9
|
-
// src/lib/
|
|
125
|
+
// src/lib/logger.ts
|
|
126
|
+
var LogLevel, Logger, logger;
|
|
127
|
+
var init_logger = __esm({
|
|
128
|
+
"src/lib/logger.ts"() {
|
|
129
|
+
init_commandSpinner();
|
|
130
|
+
LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
131
|
+
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
132
|
+
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
133
|
+
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
134
|
+
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
135
|
+
LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
|
|
136
|
+
return LogLevel2;
|
|
137
|
+
})(LogLevel || {});
|
|
138
|
+
Logger = class _Logger {
|
|
139
|
+
static instance;
|
|
140
|
+
level = 1 /* INFO */;
|
|
141
|
+
constructor() {
|
|
142
|
+
}
|
|
143
|
+
static getInstance() {
|
|
144
|
+
if (!_Logger.instance) {
|
|
145
|
+
_Logger.instance = new _Logger();
|
|
146
|
+
}
|
|
147
|
+
return _Logger.instance;
|
|
148
|
+
}
|
|
149
|
+
setLevel(level) {
|
|
150
|
+
this.level = level;
|
|
151
|
+
}
|
|
152
|
+
debug(message, ...args) {
|
|
153
|
+
if (this.level <= 0 /* DEBUG */) {
|
|
154
|
+
pauseSpinner(() => {
|
|
155
|
+
console.debug(message, ...args);
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
info(message, ...args) {
|
|
160
|
+
if (this.level <= 1 /* INFO */) {
|
|
161
|
+
pauseSpinner(() => {
|
|
162
|
+
console.log(message, ...args);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
warn(message, ...args) {
|
|
167
|
+
if (this.level <= 2 /* WARN */) {
|
|
168
|
+
pauseSpinner(() => {
|
|
169
|
+
console.warn(message, ...args);
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
error(message, ...args) {
|
|
174
|
+
if (this.level <= 3 /* ERROR */) {
|
|
175
|
+
pauseSpinner(() => {
|
|
176
|
+
console.error(message, ...args);
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
logger = Logger.getInstance();
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
function normalizeIntegrationSettings(settings) {
|
|
185
|
+
if (!settings || typeof settings !== "object") {
|
|
186
|
+
return settings;
|
|
187
|
+
}
|
|
188
|
+
if ("$ref" in settings && typeof settings.$ref === "string") {
|
|
189
|
+
return { $ref: settings.$ref };
|
|
190
|
+
}
|
|
191
|
+
if ("alias" in settings && typeof settings.alias === "string") {
|
|
192
|
+
return { $ref: `imported.integrations.${settings.alias}` };
|
|
193
|
+
}
|
|
194
|
+
return settings;
|
|
195
|
+
}
|
|
196
|
+
function normalizeTool(tool) {
|
|
197
|
+
return {
|
|
198
|
+
name: tool.name,
|
|
199
|
+
label: tool.label,
|
|
200
|
+
settings_config: tool.settings_config,
|
|
201
|
+
user_description: tool.user_description,
|
|
202
|
+
settings: normalizeIntegrationSettings(tool.settings)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
function normalizeToolkits(toolkits) {
|
|
206
|
+
if (!toolkits) {
|
|
207
|
+
return [];
|
|
208
|
+
}
|
|
209
|
+
return toolkits.map(({ toolkit, tools, label, settings_config, is_external, settings }) => ({
|
|
210
|
+
toolkit,
|
|
211
|
+
label,
|
|
212
|
+
settings_config,
|
|
213
|
+
is_external,
|
|
214
|
+
tools: tools?.map((tool) => normalizeTool(tool)),
|
|
215
|
+
settings: normalizeIntegrationSettings(settings)
|
|
216
|
+
}));
|
|
217
|
+
}
|
|
218
|
+
function normalizeMcpServers(mcpServers) {
|
|
219
|
+
if (!mcpServers) {
|
|
220
|
+
return [];
|
|
221
|
+
}
|
|
222
|
+
return mcpServers.map((mcp) => ({
|
|
223
|
+
...mcp,
|
|
224
|
+
settings: normalizeIntegrationSettings(mcp.settings),
|
|
225
|
+
mcp_connect_auth_token: normalizeIntegrationSettings(mcp.mcp_connect_auth_token)
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
function calculateChecksum(content) {
|
|
229
|
+
if (typeof content !== "string") {
|
|
230
|
+
throw new TypeError(`calculateChecksum expects string, got ${typeof content}`);
|
|
231
|
+
}
|
|
232
|
+
if (content.length === 0) {
|
|
233
|
+
logger.warn("\u26A0\uFE0F Calculating checksum of empty string");
|
|
234
|
+
}
|
|
235
|
+
return crypto.createHash("sha256").update(content, "utf8").digest("hex");
|
|
236
|
+
}
|
|
237
|
+
function normalizeAssistantConfig(assistant, buildConfig = null) {
|
|
238
|
+
return {
|
|
239
|
+
description: assistant.description || "",
|
|
240
|
+
model: assistant.model || DEFAULT_LLM_MODEL,
|
|
241
|
+
temperature: assistant.temperature,
|
|
242
|
+
top_p: assistant.top_p,
|
|
243
|
+
shared: assistant.shared,
|
|
244
|
+
is_react: assistant.is_react,
|
|
245
|
+
is_global: assistant.is_global,
|
|
246
|
+
icon_url: assistant.icon_url,
|
|
247
|
+
toolkits: normalizeToolkits(assistant.toolkits),
|
|
248
|
+
context: assistant.context || [],
|
|
249
|
+
mcp_servers: normalizeMcpServers(assistant.mcp_servers),
|
|
250
|
+
assistant_ids: assistant.assistant_ids || [],
|
|
251
|
+
sub_assistants: assistant.sub_assistants || [],
|
|
252
|
+
skills: assistant.skills || [],
|
|
253
|
+
conversation_starters: assistant.conversation_starters || [],
|
|
254
|
+
buildConfig
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function calculateAssistantConfigChecksum(assistant, buildConfig = null) {
|
|
258
|
+
const normalized = normalizeAssistantConfig(assistant, buildConfig);
|
|
259
|
+
return calculateChecksum(JSON.stringify(normalized));
|
|
260
|
+
}
|
|
261
|
+
var DEFAULT_LLM_MODEL;
|
|
262
|
+
var init_checksumUtils = __esm({
|
|
263
|
+
"src/lib/checksumUtils.ts"() {
|
|
264
|
+
init_logger();
|
|
265
|
+
DEFAULT_LLM_MODEL = "gpt-4";
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// src/lib/codemieConfigChecksums.ts
|
|
270
|
+
function createExcludeSet(keys) {
|
|
271
|
+
return new Set(keys);
|
|
272
|
+
}
|
|
273
|
+
function buildChecksumObject(src, excluded) {
|
|
274
|
+
const out = {};
|
|
275
|
+
const keys = Object.keys(src).sort();
|
|
276
|
+
for (const key of keys) {
|
|
277
|
+
if (excluded.has(key)) {
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
const value = src[key];
|
|
281
|
+
if (value !== void 0) {
|
|
282
|
+
out[key] = value;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
}
|
|
287
|
+
function calculateDatasourceConfigChecksum(datasource) {
|
|
288
|
+
const filtered = buildChecksumObject(datasource, DATASOURCE_EXCLUDED_FIELDS);
|
|
289
|
+
return calculateChecksum(JSON.stringify(filtered));
|
|
290
|
+
}
|
|
291
|
+
function calculateWorkflowConfigChecksum(workflow) {
|
|
292
|
+
const filtered = buildChecksumObject(workflow, WORKFLOW_EXCLUDED_FIELDS);
|
|
293
|
+
return calculateChecksum(JSON.stringify(filtered));
|
|
294
|
+
}
|
|
295
|
+
function calculateSkillConfigChecksum(skill) {
|
|
296
|
+
const filtered = buildChecksumObject(skill, SKILL_EXCLUDED_FIELDS);
|
|
297
|
+
return calculateChecksum(JSON.stringify(filtered));
|
|
298
|
+
}
|
|
299
|
+
var DATASOURCE_EXCLUDED_FIELDS, WORKFLOW_EXCLUDED_FIELDS, SKILL_EXCLUDED_FIELDS;
|
|
300
|
+
var init_codemieConfigChecksums = __esm({
|
|
301
|
+
"src/lib/codemieConfigChecksums.ts"() {
|
|
302
|
+
init_checksumUtils();
|
|
303
|
+
DATASOURCE_EXCLUDED_FIELDS = createExcludeSet(["force_reindex"]);
|
|
304
|
+
WORKFLOW_EXCLUDED_FIELDS = createExcludeSet(["definition"]);
|
|
305
|
+
SKILL_EXCLUDED_FIELDS = createExcludeSet(["file"]);
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
// src/lib/typeGuards.ts
|
|
310
|
+
function hasName(obj) {
|
|
311
|
+
return typeof obj === "object" && obj !== null && "name" in obj;
|
|
312
|
+
}
|
|
313
|
+
function hasSettingId(obj) {
|
|
314
|
+
return typeof obj === "object" && obj !== null && "setting_id" in obj;
|
|
315
|
+
}
|
|
316
|
+
function isResolvedIntegration(obj) {
|
|
317
|
+
return typeof obj === "object" && obj !== null && "id" in obj && typeof obj.id === "string";
|
|
318
|
+
}
|
|
319
|
+
var init_typeGuards = __esm({
|
|
320
|
+
"src/lib/typeGuards.ts"() {
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
function isChunkSummaryType(type) {
|
|
324
|
+
return CHUNK_SUMMARY_ALIASES.has(type);
|
|
325
|
+
}
|
|
326
|
+
function isFileSummaryType(type) {
|
|
327
|
+
return FILE_SUMMARY_ALIASES.has(type);
|
|
328
|
+
}
|
|
329
|
+
function normalizeCodeIndexType(datasource) {
|
|
330
|
+
if (datasource.code?.indexType) {
|
|
331
|
+
return datasource.code.indexType;
|
|
332
|
+
}
|
|
333
|
+
if (datasource.type === DataSourceType.CODE || datasource.type === DataSourceType.SUMMARY) {
|
|
334
|
+
return datasource.type;
|
|
335
|
+
}
|
|
336
|
+
if (datasource.type === DataSourceType.CHUNK_SUMMARY) {
|
|
337
|
+
return DataSourceType.CHUNK_SUMMARY;
|
|
338
|
+
}
|
|
339
|
+
if (isFileSummaryType(datasource.type)) {
|
|
340
|
+
return DataSourceType.SUMMARY;
|
|
341
|
+
}
|
|
342
|
+
if (isChunkSummaryType(datasource.type)) {
|
|
343
|
+
return DataSourceType.CHUNK_SUMMARY;
|
|
344
|
+
}
|
|
345
|
+
return void 0;
|
|
346
|
+
}
|
|
347
|
+
function validateMcpCommandArgs(s, hasTopLevelCommand) {
|
|
348
|
+
if (hasTopLevelCommand) {
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
const config = s.config;
|
|
352
|
+
const configArgs = config.args;
|
|
353
|
+
if (!Array.isArray(configArgs)) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
return configArgs.every((arg) => typeof arg === "string");
|
|
357
|
+
}
|
|
358
|
+
function isValidMcpServer(server) {
|
|
359
|
+
if (!server || typeof server !== "object") {
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
362
|
+
const s = server;
|
|
363
|
+
if (typeof s.name !== "string" || s.name.length === 0) {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
const hasTopLevelCommand = typeof s.command === "string" && s.command.length > 0;
|
|
367
|
+
const hasConfigCommand = s.config && typeof s.config === "object" && typeof s.config.command === "string" && s.config.command.length > 0;
|
|
368
|
+
const hasCommand = hasTopLevelCommand || hasConfigCommand;
|
|
369
|
+
const hasTopLevelUrl = typeof s.mcp_connect_url === "string" && s.mcp_connect_url.length > 0;
|
|
370
|
+
const hasConfigUrl = s.config && typeof s.config === "object" && typeof s.config.url === "string" && s.config.url.length > 0;
|
|
371
|
+
const hasUrl = hasTopLevelUrl || hasConfigUrl;
|
|
372
|
+
const hasSettings = s.settings != null;
|
|
373
|
+
if (hasCommand && hasUrl) {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
if (!hasCommand && !hasUrl && !hasSettings) {
|
|
377
|
+
return false;
|
|
378
|
+
}
|
|
379
|
+
if (hasCommand && !validateMcpCommandArgs(s, hasTopLevelCommand)) {
|
|
380
|
+
return false;
|
|
381
|
+
}
|
|
382
|
+
if (s.description != null && typeof s.description !== "string") {
|
|
383
|
+
return false;
|
|
384
|
+
}
|
|
385
|
+
if (s.enabled !== void 0 && typeof s.enabled !== "boolean") {
|
|
386
|
+
return false;
|
|
387
|
+
}
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
function convertMcpServers(servers) {
|
|
391
|
+
if (!servers || servers.length === 0) {
|
|
392
|
+
return void 0;
|
|
393
|
+
}
|
|
394
|
+
const validServers = servers.filter((server) => isValidMcpServer(server));
|
|
395
|
+
if (validServers.length === 0) {
|
|
396
|
+
return void 0;
|
|
397
|
+
}
|
|
398
|
+
return validServers;
|
|
399
|
+
}
|
|
400
|
+
function assistantResponseToResource(assistant) {
|
|
401
|
+
const safeName = assistant.name || assistant.id || "assistant";
|
|
402
|
+
const slug = assistant.slug || "";
|
|
403
|
+
const promptFileName = slug || String(safeName).toLowerCase().replaceAll(/\s+/g, "-");
|
|
404
|
+
const mcpServers = convertMcpServers(assistant.mcp_servers);
|
|
405
|
+
const nestedAssistants = assistant.nested_assistants;
|
|
406
|
+
const subAssistants = nestedAssistants?.map((nested) => nested.name).filter((name) => Boolean(name));
|
|
407
|
+
const categories = assistant.categories?.map((category) => category.id);
|
|
408
|
+
return {
|
|
409
|
+
name: assistant.name || safeName,
|
|
410
|
+
description: assistant.description || "",
|
|
411
|
+
prompt: `system_prompts/${promptFileName}.prompt.md`,
|
|
412
|
+
model: assistant.llm_model_type || DEFAULT_LLM_MODEL,
|
|
413
|
+
...assistant.temperature !== void 0 && assistant.temperature !== null && { temperature: assistant.temperature },
|
|
414
|
+
...assistant.top_p !== void 0 && assistant.top_p !== null && { top_p: assistant.top_p },
|
|
415
|
+
...assistant.shared !== void 0 && { shared: assistant.shared },
|
|
416
|
+
...assistant.is_react !== void 0 && { is_react: assistant.is_react },
|
|
417
|
+
...assistant.is_global !== void 0 && { is_global: assistant.is_global },
|
|
418
|
+
...assistant.icon_url && { icon_url: assistant.icon_url },
|
|
419
|
+
...assistant.conversation_starters && { conversation_starters: assistant.conversation_starters },
|
|
420
|
+
...assistant.toolkits && { toolkits: assistant.toolkits },
|
|
421
|
+
...assistant.context && { context: assistant.context },
|
|
422
|
+
...mcpServers && { mcp_servers: mcpServers },
|
|
423
|
+
...subAssistants && { sub_assistants: subAssistants },
|
|
424
|
+
...assistant.prompt_variables && { prompt_variables: assistant.prompt_variables },
|
|
425
|
+
...categories && { categories }
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
function convertCodeDatasource(datasource, base) {
|
|
429
|
+
const desc = datasource.description || "";
|
|
430
|
+
const indexType = normalizeCodeIndexType(datasource);
|
|
431
|
+
if (datasource.code) {
|
|
432
|
+
return {
|
|
433
|
+
...base,
|
|
434
|
+
type: "code",
|
|
435
|
+
description: desc,
|
|
436
|
+
link: datasource.code.link,
|
|
437
|
+
branch: datasource.code.branch,
|
|
438
|
+
...indexType && { index_type: indexType },
|
|
439
|
+
summarization_model: datasource.code.summarizationModel,
|
|
440
|
+
files_filter: datasource.code.filesFilter
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
...base,
|
|
445
|
+
type: "code",
|
|
446
|
+
description: desc,
|
|
447
|
+
...indexType && { index_type: indexType },
|
|
448
|
+
link: void 0
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
function datasourceResponseToResource(datasource, integrationAlias) {
|
|
452
|
+
const isBedrockDatasource = datasource.type === DataSourceType.BEDROCK;
|
|
453
|
+
const isChunkSummaryDatasource = isChunkSummaryType(datasource.type);
|
|
454
|
+
const isFileSummaryDatasource = isFileSummaryType(datasource.type);
|
|
455
|
+
const isCodeFamilyDatasource = datasource.type === DataSourceType.CODE || datasource.type === DataSourceType.SUMMARY || datasource.type === DataSourceType.CHUNK_SUMMARY || isChunkSummaryDatasource || isFileSummaryDatasource;
|
|
456
|
+
const settingId = integrationAlias ? `$ref:imported.integrations.${integrationAlias}.id` : datasource.setting_id ?? "";
|
|
457
|
+
const allowedWithoutSettingId = /* @__PURE__ */ new Set([DataSourceType.FILE, DataSourceType.GOOGLE, DataSourceType.BEDROCK]);
|
|
458
|
+
if (!settingId && !allowedWithoutSettingId.has(datasource.type)) {
|
|
459
|
+
logger.warn(`\u26A0\uFE0F Datasource "${datasource.name}" is missing setting_id (integration reference)`);
|
|
460
|
+
}
|
|
461
|
+
const base = {
|
|
462
|
+
name: datasource.name,
|
|
463
|
+
type: datasource.type,
|
|
464
|
+
embeddings_model: datasource.embeddings_model,
|
|
465
|
+
setting_id: settingId || void 0,
|
|
466
|
+
shared_with_project: datasource.shared_with_project
|
|
467
|
+
};
|
|
468
|
+
if (isCodeFamilyDatasource) {
|
|
469
|
+
return convertCodeDatasource(datasource, base);
|
|
470
|
+
}
|
|
471
|
+
if (datasource.type === DataSourceType.CONFLUENCE && datasource.confluence) {
|
|
472
|
+
return {
|
|
473
|
+
...base,
|
|
474
|
+
type: DataSourceType.CONFLUENCE,
|
|
475
|
+
description: datasource.description || "",
|
|
476
|
+
...datasource.confluence
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
if (datasource.type === DataSourceType.JIRA && datasource.jira) {
|
|
480
|
+
return {
|
|
481
|
+
...base,
|
|
482
|
+
type: DataSourceType.JIRA,
|
|
483
|
+
description: datasource.description || "",
|
|
484
|
+
...datasource.jira
|
|
485
|
+
};
|
|
486
|
+
}
|
|
487
|
+
if (datasource.type === DataSourceType.GOOGLE && datasource.google_doc_link) {
|
|
488
|
+
return {
|
|
489
|
+
...base,
|
|
490
|
+
type: DataSourceType.GOOGLE,
|
|
491
|
+
description: datasource.description || "",
|
|
492
|
+
google_doc: datasource.google_doc_link
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
if (datasource.type === DataSourceType.FILE) {
|
|
496
|
+
return {
|
|
497
|
+
...base,
|
|
498
|
+
type: DataSourceType.FILE,
|
|
499
|
+
description: datasource.description || ""
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
if (isBedrockDatasource) {
|
|
503
|
+
return {
|
|
504
|
+
...base,
|
|
505
|
+
type: datasource.type,
|
|
506
|
+
description: datasource.description || ""
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
logger.warn(` \u26A0\uFE0F Unknown datasource type '${datasource.type}' - saving with basic fields only`);
|
|
510
|
+
return {
|
|
511
|
+
...base,
|
|
512
|
+
description: datasource.description || ""
|
|
513
|
+
};
|
|
514
|
+
}
|
|
515
|
+
function workflowResponseToResource(workflow) {
|
|
516
|
+
const safeName = workflow.name || workflow.id || "workflow";
|
|
517
|
+
return {
|
|
518
|
+
name: workflow.name || safeName,
|
|
519
|
+
description: workflow.description || "",
|
|
520
|
+
definition: `workflows/${String(safeName).toLowerCase().replaceAll(/\s+/g, "-")}.yaml`,
|
|
521
|
+
...workflow.mode && { mode: workflow.mode },
|
|
522
|
+
...workflow.shared !== void 0 && { shared: workflow.shared },
|
|
523
|
+
...workflow.icon_url && { icon_url: workflow.icon_url }
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
function skillResponseToResource(skill) {
|
|
527
|
+
const fileName = skill.name.toLowerCase().replaceAll(/\s+/g, "-");
|
|
528
|
+
return {
|
|
529
|
+
name: skill.name,
|
|
530
|
+
description: skill.description || "",
|
|
531
|
+
file: `skills/${fileName}.skill.md`,
|
|
532
|
+
...skill.categories && skill.categories.length > 0 && { categories: skill.categories },
|
|
533
|
+
...skill.visibility && skill.visibility !== "project" && { visibility: skill.visibility }
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
function isValidCodeParams(params) {
|
|
537
|
+
return typeof params === "object" && params !== null && "link" in params && typeof params.link === "string" && params.link.length > 0;
|
|
538
|
+
}
|
|
539
|
+
function datasourceResourceToCreateParams(datasource, projectName) {
|
|
540
|
+
const {
|
|
541
|
+
$ref: _ref,
|
|
542
|
+
force_reindex: _forceReindex,
|
|
543
|
+
...sdkFields
|
|
544
|
+
} = datasource;
|
|
545
|
+
const params = {
|
|
546
|
+
...sdkFields,
|
|
547
|
+
project_name: projectName,
|
|
548
|
+
shared_with_project: datasource.shared_with_project ?? true
|
|
549
|
+
};
|
|
550
|
+
if (datasource.type === "code" && !isValidCodeParams(params)) {
|
|
551
|
+
throw new Error(
|
|
552
|
+
`Invalid code datasource "${datasource.name}": missing required field "link". Please add repository URL to datasource configuration.`
|
|
553
|
+
);
|
|
554
|
+
} else if (datasource.type === "knowledge_base_confluence" && (!("cql" in params) || !params.cql)) {
|
|
555
|
+
throw new Error(
|
|
556
|
+
`Invalid Confluence datasource "${datasource.name}": missing required field "cql". Please add CQL query to datasource configuration.`
|
|
557
|
+
);
|
|
558
|
+
} else if (datasource.type === "knowledge_base_jira" && (!("jql" in params) || !params.jql)) {
|
|
559
|
+
throw new Error(
|
|
560
|
+
`Invalid Jira datasource "${datasource.name}": missing required field "jql". Please add JQL query to datasource configuration.`
|
|
561
|
+
);
|
|
562
|
+
} else if (datasource.type === "llm_routing_google" && (!("google_doc" in params) || !params.google_doc)) {
|
|
563
|
+
throw new Error(
|
|
564
|
+
`Invalid Google Docs datasource "${datasource.name}": missing required field "google_doc". Please add Google Doc ID to datasource configuration.`
|
|
565
|
+
);
|
|
566
|
+
}
|
|
567
|
+
return params;
|
|
568
|
+
}
|
|
569
|
+
function iacToolToSdk(tool) {
|
|
570
|
+
return {
|
|
571
|
+
...tool,
|
|
572
|
+
settings_config: tool.settings_config ?? Boolean(tool.settings),
|
|
573
|
+
settings: isResolvedIntegration(tool.settings) ? tool.settings : void 0
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
function iacToolkitToSdk(toolkit) {
|
|
577
|
+
return {
|
|
578
|
+
...toolkit,
|
|
579
|
+
settings_config: toolkit.settings_config ?? Boolean(toolkit.settings),
|
|
580
|
+
is_external: toolkit.is_external ?? false,
|
|
581
|
+
tools: toolkit.tools.map((tool) => iacToolToSdk(tool)),
|
|
582
|
+
settings: isResolvedIntegration(toolkit.settings) ? toolkit.settings : void 0
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
function iacMcpServerToSdk(mcp) {
|
|
586
|
+
return {
|
|
587
|
+
...mcp,
|
|
588
|
+
enabled: mcp.enabled ?? true,
|
|
589
|
+
settings: isResolvedIntegration(mcp.settings) ? mcp.settings : void 0,
|
|
590
|
+
mcp_connect_auth_token: isResolvedIntegration(mcp.mcp_connect_auth_token) ? mcp.mcp_connect_auth_token : void 0
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
function assistantResourceToCreateParams(assistant, projectName, promptContent) {
|
|
594
|
+
const {
|
|
595
|
+
prompt: _prompt,
|
|
596
|
+
config: _config,
|
|
597
|
+
model,
|
|
598
|
+
sub_assistants: _subAssistants,
|
|
599
|
+
datasource_names: _datasourceNames,
|
|
600
|
+
skills: _skills,
|
|
601
|
+
toolkits,
|
|
602
|
+
mcp_servers: mcpServers,
|
|
603
|
+
...sdkFields
|
|
604
|
+
} = assistant;
|
|
605
|
+
return {
|
|
606
|
+
...sdkFields,
|
|
607
|
+
project: projectName,
|
|
608
|
+
llm_model_type: model,
|
|
609
|
+
system_prompt: promptContent,
|
|
610
|
+
name: assistant.name,
|
|
611
|
+
description: assistant.description,
|
|
612
|
+
conversation_starters: assistant.conversation_starters || [],
|
|
613
|
+
toolkits: (toolkits || []).map((toolkit) => iacToolkitToSdk(toolkit)),
|
|
614
|
+
context: assistant.context || [],
|
|
615
|
+
mcp_servers: (mcpServers || []).map((mcp) => iacMcpServerToSdk(mcp)),
|
|
616
|
+
assistant_ids: assistant.assistant_ids || [],
|
|
617
|
+
shared: assistant.shared ?? true,
|
|
618
|
+
prompt_variables: assistant.prompt_variables || []
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
var CHUNK_SUMMARY_ALIASES, FILE_SUMMARY_ALIASES;
|
|
622
|
+
var init_converters = __esm({
|
|
623
|
+
"src/lib/converters.ts"() {
|
|
624
|
+
init_checksumUtils();
|
|
625
|
+
init_logger();
|
|
626
|
+
init_typeGuards();
|
|
627
|
+
CHUNK_SUMMARY_ALIASES = /* @__PURE__ */ new Set(["chunk_summary", "chunk-summary"]);
|
|
628
|
+
FILE_SUMMARY_ALIASES = /* @__PURE__ */ new Set(["file_summary", "file-summary", "file-summay"]);
|
|
629
|
+
}
|
|
630
|
+
});
|
|
631
|
+
function sanitizeFileName(name, maxLength = 255) {
|
|
632
|
+
const nameWithHyphens = name.replaceAll(/[/\\]/g, "-");
|
|
633
|
+
const sanitized = nameWithHyphens.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, maxLength);
|
|
634
|
+
if (!sanitized) {
|
|
635
|
+
throw new Error(`Sanitized filename is empty for input: "${name}"`);
|
|
636
|
+
}
|
|
637
|
+
return sanitized;
|
|
638
|
+
}
|
|
639
|
+
function validateBackupDirectory(backupDir, minSpaceGB = 1) {
|
|
640
|
+
try {
|
|
641
|
+
const parentDir = path.dirname(backupDir);
|
|
642
|
+
if (!fs.existsSync(parentDir)) {
|
|
643
|
+
fs.mkdirSync(parentDir, { recursive: true });
|
|
644
|
+
}
|
|
645
|
+
const testFile = path.join(parentDir, ".write-test");
|
|
646
|
+
fs.writeFileSync(testFile, "test");
|
|
647
|
+
fs.unlinkSync(testFile);
|
|
648
|
+
try {
|
|
649
|
+
const stats = fs.statfsSync(parentDir);
|
|
650
|
+
const availableGB = stats.bavail * stats.bsize / BYTES_IN_GB;
|
|
651
|
+
if (availableGB < minSpaceGB) {
|
|
652
|
+
throw new Error(`Insufficient disk space: ${availableGB.toFixed(2)}GB available, need ${minSpaceGB}GB`);
|
|
653
|
+
}
|
|
654
|
+
} catch (error) {
|
|
655
|
+
if (error.code !== "ERR_METHOD_NOT_SUPPORTED") {
|
|
656
|
+
throw error;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
} catch (error) {
|
|
660
|
+
throw new Error(`Cannot write to backup directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function moveAtomically(tempPath, finalPath) {
|
|
664
|
+
try {
|
|
665
|
+
fs.renameSync(tempPath, finalPath);
|
|
666
|
+
} catch (error) {
|
|
667
|
+
const err = error;
|
|
668
|
+
if (err.code === "EEXIST") {
|
|
669
|
+
throw new Error(`Destination already exists: ${finalPath}`);
|
|
670
|
+
}
|
|
671
|
+
throw error;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
function cleanupDirectory(dirPath) {
|
|
675
|
+
if (fs.existsSync(dirPath)) {
|
|
676
|
+
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
function ensureDirectoryExists(filePath) {
|
|
680
|
+
const dir = path.dirname(filePath);
|
|
681
|
+
if (!fs.existsSync(dir)) {
|
|
682
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
var BYTES_IN_GB;
|
|
686
|
+
var init_fileUtils = __esm({
|
|
687
|
+
"src/lib/fileUtils.ts"() {
|
|
688
|
+
BYTES_IN_GB = 1024 ** 3;
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
// src/lib/backupTransformers.ts
|
|
693
|
+
var backupTransformers_exports = {};
|
|
694
|
+
__export(backupTransformers_exports, {
|
|
695
|
+
prepareAssistantForYaml: () => prepareAssistantForYaml,
|
|
696
|
+
prepareDatasourceForYaml: () => prepareDatasourceForYaml,
|
|
697
|
+
prepareSkillForYaml: () => prepareSkillForYaml,
|
|
698
|
+
prepareWorkflowForYaml: () => prepareWorkflowForYaml,
|
|
699
|
+
transformMcpServer: () => transformMcpServer,
|
|
700
|
+
transformTool: () => transformTool,
|
|
701
|
+
transformToolkits: () => transformToolkits
|
|
702
|
+
});
|
|
703
|
+
function transformIntegrationSettings(settings, integrationIdToAlias, integrationSpecPaths, contextLabel = "integration") {
|
|
704
|
+
const integrationId = settings.id;
|
|
705
|
+
const alias = settings.alias || integrationId && integrationIdToAlias.get(integrationId);
|
|
706
|
+
if (alias) {
|
|
707
|
+
return { $ref: `imported.integrations.${alias}` };
|
|
708
|
+
}
|
|
709
|
+
if (integrationId) {
|
|
710
|
+
logger.warn(` \u26A0\uFE0F No alias found for ${contextLabel} ${integrationId}, keeping full settings`);
|
|
711
|
+
const specPath = integrationSpecPaths?.get(integrationId);
|
|
712
|
+
if (specPath && settings.credential_values) {
|
|
713
|
+
return {
|
|
714
|
+
...settings,
|
|
715
|
+
credential_values: settings.credential_values.map(
|
|
716
|
+
(cred) => cred.key === "openapi_spec" ? { key: "openapi_spec", value: specPath } : cred
|
|
717
|
+
)
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
return settings;
|
|
722
|
+
}
|
|
723
|
+
function transformTool(tool, integrationIdToAlias, integrationSpecPaths) {
|
|
724
|
+
const result = {
|
|
725
|
+
name: tool.name,
|
|
726
|
+
settings_config: tool.settings_config
|
|
727
|
+
};
|
|
728
|
+
if (tool.label !== void 0 && tool.label !== null) {
|
|
729
|
+
result.label = tool.label;
|
|
730
|
+
}
|
|
731
|
+
if (tool.settings) {
|
|
732
|
+
result.settings = transformIntegrationSettings(tool.settings, integrationIdToAlias, integrationSpecPaths, "tool");
|
|
733
|
+
}
|
|
734
|
+
return result;
|
|
735
|
+
}
|
|
736
|
+
function transformMcpServer(mcp, integrationIdToAlias) {
|
|
737
|
+
const result = {
|
|
738
|
+
name: mcp.name,
|
|
739
|
+
description: mcp.description,
|
|
740
|
+
enabled: mcp.enabled,
|
|
741
|
+
command: mcp.command,
|
|
742
|
+
arguments: mcp.arguments,
|
|
743
|
+
config: mcp.config,
|
|
744
|
+
mcp_connect_url: mcp.mcp_connect_url,
|
|
745
|
+
mcp_connect_auth_token: mcp.mcp_connect_auth_token,
|
|
746
|
+
tools_tokens_size_limit: mcp.tools_tokens_size_limit
|
|
747
|
+
};
|
|
748
|
+
if (mcp.settings && isResolvedIntegration(mcp.settings)) {
|
|
749
|
+
const alias = mcp.settings.alias || integrationIdToAlias.get(mcp.settings.id);
|
|
750
|
+
if (alias) {
|
|
751
|
+
result.settings = { $ref: `imported.integrations.${alias}` };
|
|
752
|
+
} else {
|
|
753
|
+
logger.warn(` \u26A0\uFE0F No alias found for MCP integration ${mcp.settings.id}, keeping full settings`);
|
|
754
|
+
result.settings = mcp.settings;
|
|
755
|
+
}
|
|
756
|
+
} else if (mcp.settings) {
|
|
757
|
+
result.settings = mcp.settings;
|
|
758
|
+
}
|
|
759
|
+
if (result.mcp_connect_auth_token && isResolvedIntegration(result.mcp_connect_auth_token)) {
|
|
760
|
+
const authAlias = result.mcp_connect_auth_token.alias || integrationIdToAlias.get(result.mcp_connect_auth_token.id);
|
|
761
|
+
if (authAlias) {
|
|
762
|
+
result.mcp_connect_auth_token = { $ref: `imported.integrations.${authAlias}` };
|
|
763
|
+
} else {
|
|
764
|
+
logger.warn(
|
|
765
|
+
` \u26A0\uFE0F No alias found for MCP auth token integration ${result.mcp_connect_auth_token.id}, keeping full object`
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return result;
|
|
770
|
+
}
|
|
771
|
+
function transformToolkits(toolkits, integrationIdToAlias, integrationSpecPaths) {
|
|
772
|
+
return toolkits?.map(({ toolkit, tools, label, settings_config, is_external, settings }) => ({
|
|
773
|
+
toolkit,
|
|
774
|
+
tools: tools.map((tool) => transformTool(tool, integrationIdToAlias, integrationSpecPaths)),
|
|
775
|
+
label,
|
|
776
|
+
settings_config,
|
|
777
|
+
is_external,
|
|
778
|
+
...settings ? {
|
|
779
|
+
settings: transformIntegrationSettings(settings, integrationIdToAlias, integrationSpecPaths, "toolkit")
|
|
780
|
+
} : {}
|
|
781
|
+
}));
|
|
782
|
+
}
|
|
783
|
+
function prepareAssistantForYaml(assistant, state, integrationIdToAlias, integrationSpecPaths, skillIdToName = /* @__PURE__ */ new Map()) {
|
|
784
|
+
const transformedToolkits = transformToolkits(assistant.toolkits, integrationIdToAlias, integrationSpecPaths);
|
|
785
|
+
const transformedMcpServers = assistant.mcp_servers?.map((mcp) => transformMcpServer(mcp, integrationIdToAlias));
|
|
786
|
+
const assistantData = assistant;
|
|
787
|
+
const resolvedSkillNames = assistantData.skill_ids && assistantData.skill_ids.length > 0 ? assistantData.skill_ids.map((id) => skillIdToName.get(id)).filter((name) => Boolean(name)) : void 0;
|
|
788
|
+
const finalAssistant = {
|
|
789
|
+
...assistantResponseToResource(assistant),
|
|
790
|
+
...transformedToolkits && { toolkits: transformedToolkits },
|
|
791
|
+
...transformedMcpServers && { mcp_servers: transformedMcpServers },
|
|
792
|
+
...resolvedSkillNames && resolvedSkillNames.length > 0 && { skills: resolvedSkillNames }
|
|
793
|
+
};
|
|
794
|
+
state.resources.assistants[assistant.name] = {
|
|
795
|
+
id: assistant.id,
|
|
796
|
+
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
797
|
+
promptChecksum: calculateChecksum(assistant.system_prompt || ""),
|
|
798
|
+
configChecksum: calculateAssistantConfigChecksum(finalAssistant)
|
|
799
|
+
};
|
|
800
|
+
return finalAssistant;
|
|
801
|
+
}
|
|
802
|
+
function prepareDatasourceForYaml(datasource, state, integrationIdToAlias) {
|
|
803
|
+
const settingId = hasSettingId(datasource) ? String(datasource.setting_id || "") : "";
|
|
804
|
+
const integrationAlias = integrationIdToAlias.get(settingId);
|
|
805
|
+
const finalDatasource = datasourceResponseToResource(datasource, integrationAlias);
|
|
806
|
+
state.resources.datasources[datasource.name] = {
|
|
807
|
+
id: datasource.id,
|
|
808
|
+
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
809
|
+
configChecksum: calculateDatasourceConfigChecksum(finalDatasource)
|
|
810
|
+
};
|
|
811
|
+
return finalDatasource;
|
|
812
|
+
}
|
|
813
|
+
function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
|
|
814
|
+
const resource = workflowResponseToResource(workflow);
|
|
815
|
+
const yamlConfig = workflow.yaml_config;
|
|
816
|
+
let finalYamlContent = yamlConfig || "";
|
|
817
|
+
if (yamlConfig) {
|
|
818
|
+
try {
|
|
819
|
+
const workflowYaml = yaml6.parse(yamlConfig);
|
|
820
|
+
const transformedAssistants = workflowYaml.assistants?.map((assistant) => {
|
|
821
|
+
if (assistant.assistant_id && typeof assistant.assistant_id === "string") {
|
|
822
|
+
const assistantId = assistant.assistant_id;
|
|
823
|
+
const matchedAssistant = assistants.find(({ id }) => id === assistantId);
|
|
824
|
+
if (matchedAssistant && hasName(matchedAssistant)) {
|
|
825
|
+
const { assistant_id: _assistantId, ...rest } = assistant;
|
|
826
|
+
return { ...rest, assistant_name: matchedAssistant.name };
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return assistant;
|
|
830
|
+
});
|
|
831
|
+
if (transformedAssistants) {
|
|
832
|
+
const transformedYaml = { ...workflowYaml, assistants: transformedAssistants };
|
|
833
|
+
const fileName = `${sanitizeFileName(workflow.name)}.yaml`;
|
|
834
|
+
const filePath = path.join(backupDir, "workflows", fileName);
|
|
835
|
+
ensureDirectoryExists(filePath);
|
|
836
|
+
finalYamlContent = yaml6.stringify(transformedYaml);
|
|
837
|
+
fs.writeFileSync(filePath, finalYamlContent, "utf8");
|
|
838
|
+
}
|
|
839
|
+
} catch (error) {
|
|
840
|
+
logger.warn(
|
|
841
|
+
` \u26A0\uFE0F Failed to transform workflow YAML for ${workflow.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
state.resources.workflows[workflow.name] = {
|
|
846
|
+
id: workflow.id,
|
|
847
|
+
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
848
|
+
workflowYamlChecksum: calculateChecksum(finalYamlContent),
|
|
849
|
+
configChecksum: calculateWorkflowConfigChecksum(resource)
|
|
850
|
+
};
|
|
851
|
+
return resource;
|
|
852
|
+
}
|
|
853
|
+
function prepareSkillForYaml(skill, state) {
|
|
854
|
+
const resource = skillResponseToResource(skill);
|
|
855
|
+
if (!state.resources.skills) {
|
|
856
|
+
state.resources.skills = {};
|
|
857
|
+
}
|
|
858
|
+
state.resources.skills[skill.name] = {
|
|
859
|
+
id: skill.id,
|
|
860
|
+
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
861
|
+
contentChecksum: calculateChecksum(skill.content || ""),
|
|
862
|
+
configChecksum: calculateSkillConfigChecksum(resource)
|
|
863
|
+
};
|
|
864
|
+
return resource;
|
|
865
|
+
}
|
|
866
|
+
var init_backupTransformers = __esm({
|
|
867
|
+
"src/lib/backupTransformers.ts"() {
|
|
868
|
+
init_checksumUtils();
|
|
869
|
+
init_codemieConfigChecksums();
|
|
870
|
+
init_converters();
|
|
871
|
+
init_fileUtils();
|
|
872
|
+
init_logger();
|
|
873
|
+
init_typeGuards();
|
|
874
|
+
}
|
|
875
|
+
});
|
|
10
876
|
var CodemieConfigLoader = class {
|
|
11
877
|
appConfig;
|
|
12
878
|
constructor(appConfig) {
|
|
@@ -22,7 +888,7 @@ var CodemieConfigLoader = class {
|
|
|
22
888
|
throw new Error(`Configuration file not found: ${configPath}`);
|
|
23
889
|
}
|
|
24
890
|
const content = fs.readFileSync(configPath, "utf8");
|
|
25
|
-
const config =
|
|
891
|
+
const config = yaml6.parse(content);
|
|
26
892
|
this.resolveImports(config, rootDir);
|
|
27
893
|
this.substituteEnvVars(config);
|
|
28
894
|
this.applyDatasourceDefaults(config);
|
|
@@ -41,6 +907,17 @@ var CodemieConfigLoader = class {
|
|
|
41
907
|
}
|
|
42
908
|
return fs.readFileSync(fullPath, "utf8");
|
|
43
909
|
}
|
|
910
|
+
/**
|
|
911
|
+
* Load skill content file (.skill.md)
|
|
912
|
+
*/
|
|
913
|
+
loadSkillContent(contentPath) {
|
|
914
|
+
const { rootDir } = this.appConfig;
|
|
915
|
+
const fullPath = path.join(rootDir, contentPath);
|
|
916
|
+
if (!fs.existsSync(fullPath)) {
|
|
917
|
+
throw new Error(`Skill content file not found: ${fullPath}`);
|
|
918
|
+
}
|
|
919
|
+
return fs.readFileSync(fullPath, "utf8");
|
|
920
|
+
}
|
|
44
921
|
/**
|
|
45
922
|
* Load assistant configuration file
|
|
46
923
|
*/
|
|
@@ -51,7 +928,7 @@ var CodemieConfigLoader = class {
|
|
|
51
928
|
throw new Error(`Config file not found: ${fullPath}`);
|
|
52
929
|
}
|
|
53
930
|
const content = fs.readFileSync(fullPath, "utf8");
|
|
54
|
-
return
|
|
931
|
+
return yaml6.parse(content);
|
|
55
932
|
}
|
|
56
933
|
/**
|
|
57
934
|
* Validate that all referenced files exist
|
|
@@ -79,6 +956,17 @@ var CodemieConfigLoader = class {
|
|
|
79
956
|
}
|
|
80
957
|
}
|
|
81
958
|
}
|
|
959
|
+
if (config.resources.skills) {
|
|
960
|
+
for (const skill of config.resources.skills) {
|
|
961
|
+
const contentPath = path.join(rootDir, skill.file);
|
|
962
|
+
if (!fs.existsSync(contentPath)) {
|
|
963
|
+
errors.push(`Content file not found for skill ${skill.name}: ${skill.file}`);
|
|
964
|
+
}
|
|
965
|
+
if (!skill.description) {
|
|
966
|
+
errors.push(`Missing 'description' for skill: ${skill.name}`);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
82
970
|
return {
|
|
83
971
|
valid: errors.length === 0,
|
|
84
972
|
errors
|
|
@@ -175,7 +1063,7 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
175
1063
|
}
|
|
176
1064
|
try {
|
|
177
1065
|
const content = fs.readFileSync(filePath, "utf8");
|
|
178
|
-
const parsed =
|
|
1066
|
+
const parsed = yaml6.parse(content);
|
|
179
1067
|
if (parsed === null || parsed === void 0) {
|
|
180
1068
|
throw new TypeError(`Import file ${filePath} is empty`);
|
|
181
1069
|
}
|
|
@@ -195,28 +1083,28 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
195
1083
|
* @param rootConfig - Root config object (constant reference for resolving paths like "imported.integrations.xxx")
|
|
196
1084
|
* @param path - Current path in config tree (for error messages, e.g., "resources.assistants[0].toolkits")
|
|
197
1085
|
*/
|
|
198
|
-
resolveReferencesRecursive(current, rootConfig,
|
|
1086
|
+
resolveReferencesRecursive(current, rootConfig, path12 = "") {
|
|
199
1087
|
if (!current || typeof current !== "object") {
|
|
200
1088
|
return;
|
|
201
1089
|
}
|
|
202
1090
|
if (Array.isArray(current)) {
|
|
203
|
-
this.resolveArrayReferences(current, rootConfig,
|
|
1091
|
+
this.resolveArrayReferences(current, rootConfig, path12);
|
|
204
1092
|
return;
|
|
205
1093
|
}
|
|
206
1094
|
if ("$ref" in current && typeof current.$ref === "string") {
|
|
207
|
-
this.resolveObjectReference(current, rootConfig,
|
|
1095
|
+
this.resolveObjectReference(current, rootConfig, path12);
|
|
208
1096
|
return;
|
|
209
1097
|
}
|
|
210
|
-
this.resolveObjectProperties(current, rootConfig,
|
|
1098
|
+
this.resolveObjectProperties(current, rootConfig, path12);
|
|
211
1099
|
}
|
|
212
1100
|
/**
|
|
213
1101
|
* Resolve $ref items in arrays and flatten if they point to arrays
|
|
214
1102
|
* Example: [{ $ref: "context_definitions.repos" }] where repos is [item1, item2]
|
|
215
1103
|
* becomes [item1, item2]
|
|
216
1104
|
*/
|
|
217
|
-
resolveArrayReferences(arr, rootConfig,
|
|
1105
|
+
resolveArrayReferences(arr, rootConfig, path12) {
|
|
218
1106
|
const result = arr.flatMap((item, i) => {
|
|
219
|
-
const contextPath = `${
|
|
1107
|
+
const contextPath = `${path12}[${i}]`;
|
|
220
1108
|
if (!this.isRefObject(item) || item.$ref.startsWith("#")) {
|
|
221
1109
|
this.resolveReferencesRecursive(item, rootConfig, contextPath);
|
|
222
1110
|
return [item];
|
|
@@ -234,9 +1122,9 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
234
1122
|
* Resolve object reference: { $ref: "path" }
|
|
235
1123
|
* Replaces object with resolved data (in-place mutation)
|
|
236
1124
|
*/
|
|
237
|
-
resolveObjectReference(current, rootConfig,
|
|
1125
|
+
resolveObjectReference(current, rootConfig, path12) {
|
|
238
1126
|
const refPath = current.$ref;
|
|
239
|
-
const contextPath =
|
|
1127
|
+
const contextPath = path12 || "root";
|
|
240
1128
|
if (refPath.startsWith("#")) {
|
|
241
1129
|
return;
|
|
242
1130
|
}
|
|
@@ -246,24 +1134,24 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
246
1134
|
delete current[key];
|
|
247
1135
|
}
|
|
248
1136
|
Object.assign(current, filteredResolved);
|
|
249
|
-
this.resolveReferencesRecursive(current, rootConfig,
|
|
1137
|
+
this.resolveReferencesRecursive(current, rootConfig, path12);
|
|
250
1138
|
}
|
|
251
1139
|
/**
|
|
252
1140
|
* Resolve object properties recursively
|
|
253
1141
|
* Handles both nested objects and "$ref:path" string references
|
|
254
1142
|
*/
|
|
255
|
-
resolveObjectProperties(current, rootConfig,
|
|
1143
|
+
resolveObjectProperties(current, rootConfig, path12) {
|
|
256
1144
|
for (const [key, value] of Object.entries(current)) {
|
|
257
1145
|
if (typeof value === "string" && value.startsWith("$ref:")) {
|
|
258
1146
|
const refPath = value.slice(5);
|
|
259
1147
|
if (refPath.startsWith("#")) {
|
|
260
1148
|
continue;
|
|
261
1149
|
}
|
|
262
|
-
const contextPath =
|
|
1150
|
+
const contextPath = path12 ? `${path12}.${key}` : key;
|
|
263
1151
|
const resolved = this.resolveReference(rootConfig, refPath, contextPath);
|
|
264
1152
|
current[key] = resolved;
|
|
265
1153
|
} else {
|
|
266
|
-
this.resolveReferencesRecursive(value, rootConfig,
|
|
1154
|
+
this.resolveReferencesRecursive(value, rootConfig, path12 ? `${path12}.${key}` : key);
|
|
267
1155
|
}
|
|
268
1156
|
}
|
|
269
1157
|
}
|
|
@@ -402,161 +1290,9 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
402
1290
|
}
|
|
403
1291
|
};
|
|
404
1292
|
|
|
405
|
-
// src/lib/logger.ts
|
|
406
|
-
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
407
|
-
LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
|
|
408
|
-
LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
|
|
409
|
-
LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
|
|
410
|
-
LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
|
|
411
|
-
LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
|
|
412
|
-
return LogLevel2;
|
|
413
|
-
})(LogLevel || {});
|
|
414
|
-
var Logger = class _Logger {
|
|
415
|
-
static instance;
|
|
416
|
-
level = 1 /* INFO */;
|
|
417
|
-
constructor() {
|
|
418
|
-
}
|
|
419
|
-
static getInstance() {
|
|
420
|
-
if (!_Logger.instance) {
|
|
421
|
-
_Logger.instance = new _Logger();
|
|
422
|
-
}
|
|
423
|
-
return _Logger.instance;
|
|
424
|
-
}
|
|
425
|
-
setLevel(level) {
|
|
426
|
-
this.level = level;
|
|
427
|
-
}
|
|
428
|
-
debug(message, ...args) {
|
|
429
|
-
if (this.level <= 0 /* DEBUG */) {
|
|
430
|
-
console.debug(message, ...args);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
info(message, ...args) {
|
|
434
|
-
if (this.level <= 1 /* INFO */) {
|
|
435
|
-
console.log(message, ...args);
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
warn(message, ...args) {
|
|
439
|
-
if (this.level <= 2 /* WARN */) {
|
|
440
|
-
console.warn(message, ...args);
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
error(message, ...args) {
|
|
444
|
-
if (this.level <= 3 /* ERROR */) {
|
|
445
|
-
console.error(message, ...args);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
};
|
|
449
|
-
var logger = Logger.getInstance();
|
|
450
|
-
|
|
451
|
-
// src/lib/checksumUtils.ts
|
|
452
|
-
var DEFAULT_LLM_MODEL = "gpt-4";
|
|
453
|
-
function normalizeIntegrationSettings(settings) {
|
|
454
|
-
if (!settings || typeof settings !== "object") {
|
|
455
|
-
return settings;
|
|
456
|
-
}
|
|
457
|
-
if ("$ref" in settings && typeof settings.$ref === "string") {
|
|
458
|
-
return { $ref: settings.$ref };
|
|
459
|
-
}
|
|
460
|
-
if ("alias" in settings && typeof settings.alias === "string") {
|
|
461
|
-
return { $ref: `imported.integrations.${settings.alias}` };
|
|
462
|
-
}
|
|
463
|
-
return settings;
|
|
464
|
-
}
|
|
465
|
-
function normalizeTool(tool) {
|
|
466
|
-
return {
|
|
467
|
-
name: tool.name,
|
|
468
|
-
label: tool.label,
|
|
469
|
-
settings_config: tool.settings_config,
|
|
470
|
-
user_description: tool.user_description,
|
|
471
|
-
settings: normalizeIntegrationSettings(tool.settings)
|
|
472
|
-
};
|
|
473
|
-
}
|
|
474
|
-
function normalizeToolkits(toolkits) {
|
|
475
|
-
if (!toolkits) {
|
|
476
|
-
return [];
|
|
477
|
-
}
|
|
478
|
-
return toolkits.map(({ toolkit, tools, label, settings_config, is_external, settings }) => ({
|
|
479
|
-
toolkit,
|
|
480
|
-
label,
|
|
481
|
-
settings_config,
|
|
482
|
-
is_external,
|
|
483
|
-
tools: tools?.map((tool) => normalizeTool(tool)),
|
|
484
|
-
settings: normalizeIntegrationSettings(settings)
|
|
485
|
-
}));
|
|
486
|
-
}
|
|
487
|
-
function normalizeMcpServers(mcpServers) {
|
|
488
|
-
if (!mcpServers) {
|
|
489
|
-
return [];
|
|
490
|
-
}
|
|
491
|
-
return mcpServers.map((mcp) => ({
|
|
492
|
-
...mcp,
|
|
493
|
-
settings: normalizeIntegrationSettings(mcp.settings),
|
|
494
|
-
mcp_connect_auth_token: normalizeIntegrationSettings(mcp.mcp_connect_auth_token)
|
|
495
|
-
}));
|
|
496
|
-
}
|
|
497
|
-
function calculateChecksum(content) {
|
|
498
|
-
if (typeof content !== "string") {
|
|
499
|
-
throw new TypeError(`calculateChecksum expects string, got ${typeof content}`);
|
|
500
|
-
}
|
|
501
|
-
if (content.length === 0) {
|
|
502
|
-
logger.warn("\u26A0\uFE0F Calculating checksum of empty string");
|
|
503
|
-
}
|
|
504
|
-
return crypto.createHash("sha256").update(content, "utf8").digest("hex");
|
|
505
|
-
}
|
|
506
|
-
function normalizeAssistantConfig(assistant, buildConfig = null) {
|
|
507
|
-
return {
|
|
508
|
-
description: assistant.description || "",
|
|
509
|
-
model: assistant.model || DEFAULT_LLM_MODEL,
|
|
510
|
-
temperature: assistant.temperature,
|
|
511
|
-
top_p: assistant.top_p,
|
|
512
|
-
shared: assistant.shared,
|
|
513
|
-
is_react: assistant.is_react,
|
|
514
|
-
is_global: assistant.is_global,
|
|
515
|
-
icon_url: assistant.icon_url,
|
|
516
|
-
toolkits: normalizeToolkits(assistant.toolkits),
|
|
517
|
-
context: assistant.context || [],
|
|
518
|
-
mcp_servers: normalizeMcpServers(assistant.mcp_servers),
|
|
519
|
-
assistant_ids: assistant.assistant_ids || [],
|
|
520
|
-
sub_assistants: assistant.sub_assistants || [],
|
|
521
|
-
conversation_starters: assistant.conversation_starters || [],
|
|
522
|
-
buildConfig
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
function calculateAssistantConfigChecksum(assistant, buildConfig = null) {
|
|
526
|
-
const normalized = normalizeAssistantConfig(assistant, buildConfig);
|
|
527
|
-
return calculateChecksum(JSON.stringify(normalized));
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// src/lib/codemieConfigChecksums.ts
|
|
531
|
-
function createExcludeSet(keys) {
|
|
532
|
-
return new Set(keys);
|
|
533
|
-
}
|
|
534
|
-
var DATASOURCE_EXCLUDED_FIELDS = createExcludeSet(["force_reindex"]);
|
|
535
|
-
var WORKFLOW_EXCLUDED_FIELDS = createExcludeSet(["definition"]);
|
|
536
|
-
function buildChecksumObject(src, excluded) {
|
|
537
|
-
const out = {};
|
|
538
|
-
const keys = Object.keys(src).sort();
|
|
539
|
-
for (const key of keys) {
|
|
540
|
-
if (excluded.has(key)) {
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
const value = src[key];
|
|
544
|
-
if (value !== void 0) {
|
|
545
|
-
out[key] = value;
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
return out;
|
|
549
|
-
}
|
|
550
|
-
function calculateDatasourceConfigChecksum(datasource) {
|
|
551
|
-
const filtered = buildChecksumObject(datasource, DATASOURCE_EXCLUDED_FIELDS);
|
|
552
|
-
return calculateChecksum(JSON.stringify(filtered));
|
|
553
|
-
}
|
|
554
|
-
function calculateWorkflowConfigChecksum(workflow) {
|
|
555
|
-
const filtered = buildChecksumObject(workflow, WORKFLOW_EXCLUDED_FIELDS);
|
|
556
|
-
return calculateChecksum(JSON.stringify(filtered));
|
|
557
|
-
}
|
|
558
|
-
|
|
559
1293
|
// src/lib/stateManager.ts
|
|
1294
|
+
init_checksumUtils();
|
|
1295
|
+
init_codemieConfigChecksums();
|
|
560
1296
|
var StateManager = class {
|
|
561
1297
|
statePath;
|
|
562
1298
|
constructor(appConfig) {
|
|
@@ -576,7 +1312,8 @@ var StateManager = class {
|
|
|
576
1312
|
state.resources = {
|
|
577
1313
|
assistants: {},
|
|
578
1314
|
datasources: {},
|
|
579
|
-
workflows: {}
|
|
1315
|
+
workflows: {},
|
|
1316
|
+
skills: {}
|
|
580
1317
|
};
|
|
581
1318
|
}
|
|
582
1319
|
if (!state.resources.assistants) {
|
|
@@ -588,6 +1325,9 @@ var StateManager = class {
|
|
|
588
1325
|
if (!state.resources.workflows) {
|
|
589
1326
|
state.resources.workflows = {};
|
|
590
1327
|
}
|
|
1328
|
+
if (!state.resources.skills) {
|
|
1329
|
+
state.resources.skills = {};
|
|
1330
|
+
}
|
|
591
1331
|
return state;
|
|
592
1332
|
}
|
|
593
1333
|
/**
|
|
@@ -612,7 +1352,8 @@ var StateManager = class {
|
|
|
612
1352
|
resources: {
|
|
613
1353
|
assistants: {},
|
|
614
1354
|
datasources: {},
|
|
615
|
-
workflows: {}
|
|
1355
|
+
workflows: {},
|
|
1356
|
+
skills: {}
|
|
616
1357
|
}
|
|
617
1358
|
};
|
|
618
1359
|
}
|
|
@@ -707,19 +1448,20 @@ var StateManager = class {
|
|
|
707
1448
|
}
|
|
708
1449
|
/**
|
|
709
1450
|
* Get all managed resources (for cleanup/destroy)
|
|
710
|
-
* Returns: { assistants: [name1, name2], datasources: [name1], workflows: [name1] }
|
|
1451
|
+
* Returns: { assistants: [name1, name2], datasources: [name1], workflows: [name1], skills: [name1] }
|
|
711
1452
|
*/
|
|
712
1453
|
getAllManagedResources() {
|
|
713
1454
|
const state = this.loadState();
|
|
714
1455
|
return {
|
|
715
1456
|
assistants: Object.keys(state.resources.assistants),
|
|
716
1457
|
datasources: Object.keys(state.resources.datasources),
|
|
717
|
-
workflows: Object.keys(state.resources.workflows)
|
|
1458
|
+
workflows: Object.keys(state.resources.workflows),
|
|
1459
|
+
skills: Object.keys(state.resources.skills || {})
|
|
718
1460
|
};
|
|
719
1461
|
}
|
|
720
1462
|
/**
|
|
721
1463
|
* Check if a resource is managed by IaC (exists in state.json)
|
|
722
|
-
* @param type Resource type ('assistant', 'datasource', 'workflow')
|
|
1464
|
+
* @param type Resource type ('assistant', 'datasource', 'workflow', 'skill')
|
|
723
1465
|
* @param name Resource name
|
|
724
1466
|
*/
|
|
725
1467
|
isManagedResource(type, name) {
|
|
@@ -734,6 +1476,9 @@ var StateManager = class {
|
|
|
734
1476
|
case "workflow": {
|
|
735
1477
|
return name in state.resources.workflows;
|
|
736
1478
|
}
|
|
1479
|
+
case "skill": {
|
|
1480
|
+
return name in (state.resources.skills || {});
|
|
1481
|
+
}
|
|
737
1482
|
default: {
|
|
738
1483
|
return false;
|
|
739
1484
|
}
|
|
@@ -754,11 +1499,47 @@ var StateManager = class {
|
|
|
754
1499
|
case "workflow": {
|
|
755
1500
|
return state.resources.workflows[name]?.id;
|
|
756
1501
|
}
|
|
1502
|
+
case "skill": {
|
|
1503
|
+
return (state.resources.skills || {})[name]?.id;
|
|
1504
|
+
}
|
|
757
1505
|
default: {
|
|
758
1506
|
return void 0;
|
|
759
1507
|
}
|
|
760
1508
|
}
|
|
761
1509
|
}
|
|
1510
|
+
/**
|
|
1511
|
+
* Update skill state (keyed by NAME)
|
|
1512
|
+
*/
|
|
1513
|
+
updateSkillState(name, id, content, skillResource) {
|
|
1514
|
+
const state = this.loadState();
|
|
1515
|
+
if (!state.resources.skills) {
|
|
1516
|
+
state.resources.skills = {};
|
|
1517
|
+
}
|
|
1518
|
+
state.resources.skills[name] = {
|
|
1519
|
+
id,
|
|
1520
|
+
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1521
|
+
contentChecksum: calculateChecksum(content),
|
|
1522
|
+
configChecksum: calculateSkillConfigChecksum(skillResource)
|
|
1523
|
+
};
|
|
1524
|
+
this.saveState(state);
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Get skill state by NAME
|
|
1528
|
+
*/
|
|
1529
|
+
getSkillState(name) {
|
|
1530
|
+
const state = this.loadState();
|
|
1531
|
+
return (state.resources.skills || {})[name];
|
|
1532
|
+
}
|
|
1533
|
+
/**
|
|
1534
|
+
* Delete skill state by NAME
|
|
1535
|
+
*/
|
|
1536
|
+
deleteSkillState(name) {
|
|
1537
|
+
const state = this.loadState();
|
|
1538
|
+
if (state.resources.skills) {
|
|
1539
|
+
delete state.resources.skills[name];
|
|
1540
|
+
}
|
|
1541
|
+
this.saveState(state);
|
|
1542
|
+
}
|
|
762
1543
|
};
|
|
763
1544
|
async function createClient(config) {
|
|
764
1545
|
const client = new CodeMieClient({
|
|
@@ -771,16 +1552,26 @@ async function createClient(config) {
|
|
|
771
1552
|
password: config.environment.password,
|
|
772
1553
|
verify_ssl: true
|
|
773
1554
|
});
|
|
774
|
-
|
|
1555
|
+
try {
|
|
1556
|
+
await client.initialize();
|
|
1557
|
+
} catch (err) {
|
|
1558
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
1559
|
+
throw new Error(
|
|
1560
|
+
`Failed to initialize Codemie client (auth: ${config.environment.auth_server_url}, api: ${config.environment.codemie_api_url}): ${reason}`
|
|
1561
|
+
);
|
|
1562
|
+
}
|
|
775
1563
|
return client;
|
|
776
1564
|
}
|
|
777
1565
|
|
|
778
1566
|
// src/lib/cleanupManager.ts
|
|
1567
|
+
init_logger();
|
|
779
1568
|
var CleanupManager = class {
|
|
780
1569
|
constructor(client, stateManager) {
|
|
781
1570
|
this.client = client;
|
|
782
1571
|
this.stateManager = stateManager;
|
|
783
1572
|
}
|
|
1573
|
+
client;
|
|
1574
|
+
stateManager;
|
|
784
1575
|
/**
|
|
785
1576
|
* Check if an error indicates that a resource was not found on the platform
|
|
786
1577
|
* Handles both proper 404 responses
|
|
@@ -821,10 +1612,12 @@ var CleanupManager = class {
|
|
|
821
1612
|
const configAssistantNames = new Set((config.resources.assistants || []).map(({ name }) => name));
|
|
822
1613
|
const configDatasourceNames = new Set((config.resources.datasources || []).map(({ name }) => name));
|
|
823
1614
|
const configWorkflowNames = new Set((config.resources.workflows || []).map(({ name }) => name));
|
|
1615
|
+
const configSkillNames = new Set((config.resources.skills || []).map(({ name }) => name));
|
|
824
1616
|
return {
|
|
825
1617
|
assistants: managedResources.assistants.filter((name) => !configAssistantNames.has(name)),
|
|
826
1618
|
datasources: managedResources.datasources.filter((name) => !configDatasourceNames.has(name)),
|
|
827
|
-
workflows: managedResources.workflows.filter((name) => !configWorkflowNames.has(name))
|
|
1619
|
+
workflows: managedResources.workflows.filter((name) => !configWorkflowNames.has(name)),
|
|
1620
|
+
skills: managedResources.skills.filter((name) => !configSkillNames.has(name))
|
|
828
1621
|
};
|
|
829
1622
|
}
|
|
830
1623
|
/**
|
|
@@ -838,7 +1631,8 @@ var CleanupManager = class {
|
|
|
838
1631
|
deleted: {
|
|
839
1632
|
assistants: [],
|
|
840
1633
|
datasources: [],
|
|
841
|
-
workflows: []
|
|
1634
|
+
workflows: [],
|
|
1635
|
+
skills: []
|
|
842
1636
|
},
|
|
843
1637
|
errors: []
|
|
844
1638
|
};
|
|
@@ -907,352 +1701,131 @@ var CleanupManager = class {
|
|
|
907
1701
|
this.stateManager.deleteWorkflowState(name);
|
|
908
1702
|
result.deleted.workflows.push(name);
|
|
909
1703
|
} catch (error) {
|
|
910
|
-
result.errors.push({
|
|
911
|
-
type: "workflow",
|
|
912
|
-
name,
|
|
913
|
-
error: error instanceof Error ? error.message : String(error)
|
|
914
|
-
});
|
|
915
|
-
logger.error(
|
|
916
|
-
` \u274C Failed to delete workflow ${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
917
|
-
);
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
// src/lib/paginationUtils.ts
|
|
945
|
-
async function findResourceByName(listFn, name, resourceType) {
|
|
946
|
-
let page = 0;
|
|
947
|
-
const perPage = 100;
|
|
948
|
-
let totalChecked = 0;
|
|
949
|
-
while (true) {
|
|
950
|
-
const resources = await listFn({ page, per_page: perPage });
|
|
951
|
-
totalChecked += resources.length;
|
|
952
|
-
const found = resources.find((r) => r.name === name);
|
|
953
|
-
if (found && found.id) {
|
|
954
|
-
return found;
|
|
955
|
-
}
|
|
956
|
-
if (resources.length < perPage) {
|
|
957
|
-
logger.warn(
|
|
958
|
-
` \u26A0\uFE0F ${resourceType} "${name}" not found after checking ${totalChecked} resources across ${page + 1} page(s)`
|
|
959
|
-
);
|
|
960
|
-
return null;
|
|
961
|
-
}
|
|
962
|
-
page++;
|
|
963
|
-
if (page > 1e3) {
|
|
964
|
-
logger.warn(` \u26A0\uFE0F Stopped pagination after 1000 pages (${totalChecked} resources checked)`);
|
|
965
|
-
return null;
|
|
966
|
-
}
|
|
967
|
-
}
|
|
968
|
-
}
|
|
969
|
-
async function findDatasourceByName(client, name) {
|
|
970
|
-
const datasource = await findResourceByName((params) => client.datasources.list(params), name, "datasource");
|
|
971
|
-
return datasource?.id || null;
|
|
972
|
-
}
|
|
973
|
-
async function findAssistantByName(client, name) {
|
|
974
|
-
const assistant = await findResourceByName((params) => client.assistants.list(params), name, "assistant");
|
|
975
|
-
return assistant?.id || null;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// src/lib/assistantHelpers.ts
|
|
979
|
-
async function createAssistantAndGetId(client, params, slug) {
|
|
980
|
-
await client.assistants.create(params);
|
|
981
|
-
if (slug) {
|
|
982
|
-
const created = await client.assistants.getBySlug(slug);
|
|
983
|
-
if (!created?.id) {
|
|
984
|
-
throw new Error(`Assistant created but not found by slug: ${slug}`);
|
|
985
|
-
}
|
|
986
|
-
return created.id;
|
|
987
|
-
}
|
|
988
|
-
const assistantId = await findAssistantByName(client, params.name);
|
|
989
|
-
if (!assistantId) {
|
|
990
|
-
throw new Error(`Assistant created but not found by name: ${params.name}`);
|
|
991
|
-
}
|
|
992
|
-
return assistantId;
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
// src/lib/typeGuards.ts
|
|
996
|
-
function hasName(obj) {
|
|
997
|
-
return typeof obj === "object" && obj !== null && "name" in obj;
|
|
998
|
-
}
|
|
999
|
-
function hasSettingId(obj) {
|
|
1000
|
-
return typeof obj === "object" && obj !== null && "setting_id" in obj;
|
|
1001
|
-
}
|
|
1002
|
-
function isResolvedIntegration(obj) {
|
|
1003
|
-
return typeof obj === "object" && obj !== null && "id" in obj && typeof obj.id === "string";
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// src/lib/converters.ts
|
|
1007
|
-
function validateMcpCommandArgs(s, hasTopLevelCommand) {
|
|
1008
|
-
if (hasTopLevelCommand) {
|
|
1009
|
-
return true;
|
|
1010
|
-
}
|
|
1011
|
-
const config = s.config;
|
|
1012
|
-
const configArgs = config.args;
|
|
1013
|
-
if (!Array.isArray(configArgs)) {
|
|
1014
|
-
return false;
|
|
1015
|
-
}
|
|
1016
|
-
return configArgs.every((arg) => typeof arg === "string");
|
|
1017
|
-
}
|
|
1018
|
-
function isValidMcpServer(server) {
|
|
1019
|
-
if (!server || typeof server !== "object") {
|
|
1020
|
-
return false;
|
|
1021
|
-
}
|
|
1022
|
-
const s = server;
|
|
1023
|
-
if (typeof s.name !== "string" || s.name.length === 0) {
|
|
1024
|
-
return false;
|
|
1025
|
-
}
|
|
1026
|
-
const hasTopLevelCommand = typeof s.command === "string" && s.command.length > 0;
|
|
1027
|
-
const hasConfigCommand = s.config && typeof s.config === "object" && typeof s.config.command === "string" && s.config.command.length > 0;
|
|
1028
|
-
const hasCommand = hasTopLevelCommand || hasConfigCommand;
|
|
1029
|
-
const hasTopLevelUrl = typeof s.mcp_connect_url === "string" && s.mcp_connect_url.length > 0;
|
|
1030
|
-
const hasConfigUrl = s.config && typeof s.config === "object" && typeof s.config.url === "string" && s.config.url.length > 0;
|
|
1031
|
-
const hasUrl = hasTopLevelUrl || hasConfigUrl;
|
|
1032
|
-
const hasSettings = s.settings != null;
|
|
1033
|
-
if (hasCommand && hasUrl) {
|
|
1034
|
-
return false;
|
|
1035
|
-
}
|
|
1036
|
-
if (!hasCommand && !hasUrl && !hasSettings) {
|
|
1037
|
-
return false;
|
|
1038
|
-
}
|
|
1039
|
-
if (hasCommand && !validateMcpCommandArgs(s, hasTopLevelCommand)) {
|
|
1040
|
-
return false;
|
|
1041
|
-
}
|
|
1042
|
-
if (s.description != null && typeof s.description !== "string") {
|
|
1043
|
-
return false;
|
|
1044
|
-
}
|
|
1045
|
-
if (s.enabled !== void 0 && typeof s.enabled !== "boolean") {
|
|
1046
|
-
return false;
|
|
1047
|
-
}
|
|
1048
|
-
return true;
|
|
1049
|
-
}
|
|
1050
|
-
function convertMcpServers(servers) {
|
|
1051
|
-
if (!servers || servers.length === 0) {
|
|
1052
|
-
return void 0;
|
|
1053
|
-
}
|
|
1054
|
-
const validServers = servers.filter((server) => isValidMcpServer(server));
|
|
1055
|
-
if (validServers.length === 0) {
|
|
1056
|
-
return void 0;
|
|
1057
|
-
}
|
|
1058
|
-
return validServers;
|
|
1059
|
-
}
|
|
1060
|
-
function assistantResponseToResource(assistant) {
|
|
1061
|
-
const slug = assistant.slug || "";
|
|
1062
|
-
const promptFileName = slug || assistant.name.toLowerCase().replaceAll(/\s+/g, "-");
|
|
1063
|
-
const mcpServers = convertMcpServers(assistant.mcp_servers);
|
|
1064
|
-
const nestedAssistants = assistant.nested_assistants;
|
|
1065
|
-
const subAssistants = nestedAssistants?.map((nested) => nested.name).filter((name) => Boolean(name));
|
|
1066
|
-
const categories = assistant.categories?.map((category) => category.id);
|
|
1067
|
-
return {
|
|
1068
|
-
name: assistant.name,
|
|
1069
|
-
description: assistant.description || "",
|
|
1070
|
-
prompt: `system_prompts/${promptFileName}.prompt.md`,
|
|
1071
|
-
model: assistant.llm_model_type || DEFAULT_LLM_MODEL,
|
|
1072
|
-
...assistant.temperature !== void 0 && assistant.temperature !== null && { temperature: assistant.temperature },
|
|
1073
|
-
...assistant.top_p !== void 0 && assistant.top_p !== null && { top_p: assistant.top_p },
|
|
1074
|
-
...assistant.shared !== void 0 && { shared: assistant.shared },
|
|
1075
|
-
...assistant.is_react !== void 0 && { is_react: assistant.is_react },
|
|
1076
|
-
...assistant.is_global !== void 0 && { is_global: assistant.is_global },
|
|
1077
|
-
...assistant.icon_url && { icon_url: assistant.icon_url },
|
|
1078
|
-
...assistant.conversation_starters && { conversation_starters: assistant.conversation_starters },
|
|
1079
|
-
...assistant.toolkits && { toolkits: assistant.toolkits },
|
|
1080
|
-
...assistant.context && { context: assistant.context },
|
|
1081
|
-
...mcpServers && { mcp_servers: mcpServers },
|
|
1082
|
-
...subAssistants && { sub_assistants: subAssistants },
|
|
1083
|
-
...assistant.prompt_variables && { prompt_variables: assistant.prompt_variables },
|
|
1084
|
-
...categories && { categories }
|
|
1085
|
-
};
|
|
1086
|
-
}
|
|
1087
|
-
function convertCodeDatasource(datasource, base) {
|
|
1088
|
-
const desc = datasource.description || "";
|
|
1089
|
-
if (datasource.code) {
|
|
1090
|
-
return {
|
|
1091
|
-
...base,
|
|
1092
|
-
type: "code",
|
|
1093
|
-
description: desc,
|
|
1094
|
-
link: datasource.code.link,
|
|
1095
|
-
branch: datasource.code.branch,
|
|
1096
|
-
index_type: datasource.code.indexType,
|
|
1097
|
-
summarization_model: datasource.code.summarizationModel,
|
|
1098
|
-
files_filter: datasource.code.filesFilter
|
|
1099
|
-
};
|
|
1100
|
-
}
|
|
1101
|
-
return {
|
|
1102
|
-
...base,
|
|
1103
|
-
type: "code",
|
|
1104
|
-
description: desc,
|
|
1105
|
-
link: void 0
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
function datasourceResponseToResource(datasource, integrationAlias) {
|
|
1109
|
-
const settingId = integrationAlias ? `$ref:imported.integrations.${integrationAlias}.id` : datasource.setting_id ?? "";
|
|
1110
|
-
const allowedWithoutSettingId = /* @__PURE__ */ new Set(["knowledge_base_file", "llm_routing_google"]);
|
|
1111
|
-
if (!settingId && !allowedWithoutSettingId.has(datasource.type)) {
|
|
1112
|
-
logger.warn(`\u26A0\uFE0F Datasource "${datasource.name}" is missing setting_id (integration reference)`);
|
|
1113
|
-
}
|
|
1114
|
-
const base = {
|
|
1115
|
-
name: datasource.name,
|
|
1116
|
-
type: datasource.type,
|
|
1117
|
-
embeddings_model: datasource.embeddings_model,
|
|
1118
|
-
setting_id: settingId || void 0,
|
|
1119
|
-
shared_with_project: datasource.shared_with_project
|
|
1120
|
-
};
|
|
1121
|
-
if (datasource.type === DataSourceType.CODE) {
|
|
1122
|
-
return convertCodeDatasource(datasource, base);
|
|
1123
|
-
}
|
|
1124
|
-
if (datasource.type === DataSourceType.CONFLUENCE && datasource.confluence) {
|
|
1125
|
-
return {
|
|
1126
|
-
...base,
|
|
1127
|
-
type: DataSourceType.CONFLUENCE,
|
|
1128
|
-
description: datasource.description || "",
|
|
1129
|
-
...datasource.confluence
|
|
1130
|
-
};
|
|
1131
|
-
}
|
|
1132
|
-
if (datasource.type === DataSourceType.JIRA && datasource.jira) {
|
|
1133
|
-
return {
|
|
1134
|
-
...base,
|
|
1135
|
-
type: DataSourceType.JIRA,
|
|
1136
|
-
description: datasource.description || "",
|
|
1137
|
-
...datasource.jira
|
|
1138
|
-
};
|
|
1139
|
-
}
|
|
1140
|
-
if (datasource.type === DataSourceType.GOOGLE && datasource.google_doc_link) {
|
|
1141
|
-
return {
|
|
1142
|
-
...base,
|
|
1143
|
-
type: DataSourceType.GOOGLE,
|
|
1144
|
-
description: datasource.description || "",
|
|
1145
|
-
google_doc: datasource.google_doc_link
|
|
1146
|
-
};
|
|
1704
|
+
result.errors.push({
|
|
1705
|
+
type: "workflow",
|
|
1706
|
+
name,
|
|
1707
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1708
|
+
});
|
|
1709
|
+
logger.error(
|
|
1710
|
+
` \u274C Failed to delete workflow ${name}: ${error instanceof Error ? error.message : String(error)}`
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
for (const name of orphaned.skills || []) {
|
|
1715
|
+
try {
|
|
1716
|
+
if (!this.stateManager.isManagedResource("skill", name)) {
|
|
1717
|
+
logger.info(` \u26A0\uFE0F Skipping ${name} - not in state (safety check)`);
|
|
1718
|
+
continue;
|
|
1719
|
+
}
|
|
1720
|
+
const id = this.stateManager.getIdByName("skill", name);
|
|
1721
|
+
if (!id) {
|
|
1722
|
+
logger.info(` \u26A0\uFE0F Skipping ${name} - no ID in state`);
|
|
1723
|
+
continue;
|
|
1724
|
+
}
|
|
1725
|
+
await this.deleteResourceSafely("skill", name, id, () => this.client.skills.delete(id));
|
|
1726
|
+
this.stateManager.deleteSkillState(name);
|
|
1727
|
+
result.deleted.skills.push(name);
|
|
1728
|
+
} catch (error) {
|
|
1729
|
+
result.errors.push({
|
|
1730
|
+
type: "skill",
|
|
1731
|
+
name,
|
|
1732
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1733
|
+
});
|
|
1734
|
+
logger.error(` \u274C Failed to delete skill ${name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
1735
|
+
}
|
|
1736
|
+
}
|
|
1737
|
+
return result;
|
|
1147
1738
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1739
|
+
/**
|
|
1740
|
+
* Get summary of orphaned resources
|
|
1741
|
+
*/
|
|
1742
|
+
getOrphanedSummary(orphaned) {
|
|
1743
|
+
const total = orphaned.assistants.length + orphaned.datasources.length + orphaned.workflows.length + (orphaned.skills?.length ?? 0);
|
|
1744
|
+
if (total === 0) {
|
|
1745
|
+
return "No orphaned resources found";
|
|
1746
|
+
}
|
|
1747
|
+
const parts = [];
|
|
1748
|
+
if (orphaned.assistants.length > 0) {
|
|
1749
|
+
parts.push(`${orphaned.assistants.length} assistant(s)`);
|
|
1750
|
+
}
|
|
1751
|
+
if (orphaned.datasources.length > 0) {
|
|
1752
|
+
parts.push(`${orphaned.datasources.length} datasource(s)`);
|
|
1753
|
+
}
|
|
1754
|
+
if (orphaned.workflows.length > 0) {
|
|
1755
|
+
parts.push(`${orphaned.workflows.length} workflow(s)`);
|
|
1756
|
+
}
|
|
1757
|
+
if ((orphaned.skills?.length ?? 0) > 0) {
|
|
1758
|
+
parts.push(`${orphaned.skills.length} skill(s)`);
|
|
1759
|
+
}
|
|
1760
|
+
return `Found ${total} orphaned resource(s): ${parts.join(", ")}`;
|
|
1154
1761
|
}
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
};
|
|
1185
|
-
if (datasource.type === "code" && !isValidCodeParams(params)) {
|
|
1186
|
-
throw new Error(
|
|
1187
|
-
`Invalid code datasource "${datasource.name}": missing required field "link". Please add repository URL to datasource configuration.`
|
|
1188
|
-
);
|
|
1189
|
-
} else if (datasource.type === "knowledge_base_confluence" && (!("cql" in params) || !params.cql)) {
|
|
1190
|
-
throw new Error(
|
|
1191
|
-
`Invalid Confluence datasource "${datasource.name}": missing required field "cql". Please add CQL query to datasource configuration.`
|
|
1192
|
-
);
|
|
1193
|
-
} else if (datasource.type === "knowledge_base_jira" && (!("jql" in params) || !params.jql)) {
|
|
1194
|
-
throw new Error(
|
|
1195
|
-
`Invalid Jira datasource "${datasource.name}": missing required field "jql". Please add JQL query to datasource configuration.`
|
|
1196
|
-
);
|
|
1197
|
-
} else if (datasource.type === "llm_routing_google" && (!("google_doc" in params) || !params.google_doc)) {
|
|
1198
|
-
throw new Error(
|
|
1199
|
-
`Invalid Google Docs datasource "${datasource.name}": missing required field "google_doc". Please add Google Doc ID to datasource configuration.`
|
|
1200
|
-
);
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
// src/index.ts
|
|
1765
|
+
init_logger();
|
|
1766
|
+
|
|
1767
|
+
// src/lib/paginationUtils.ts
|
|
1768
|
+
init_logger();
|
|
1769
|
+
async function findResourceByName(listFn, name, resourceType) {
|
|
1770
|
+
let page = 0;
|
|
1771
|
+
const perPage = 100;
|
|
1772
|
+
let totalChecked = 0;
|
|
1773
|
+
while (true) {
|
|
1774
|
+
const resources = await listFn({ page, per_page: perPage });
|
|
1775
|
+
totalChecked += resources.length;
|
|
1776
|
+
const found = resources.find((r) => r.name === name);
|
|
1777
|
+
if (found && found.id) {
|
|
1778
|
+
return found;
|
|
1779
|
+
}
|
|
1780
|
+
if (resources.length < perPage) {
|
|
1781
|
+
logger.warn(
|
|
1782
|
+
` \u26A0\uFE0F ${resourceType} "${name}" not found after checking ${totalChecked} resources across ${page + 1} page(s)`
|
|
1783
|
+
);
|
|
1784
|
+
return null;
|
|
1785
|
+
}
|
|
1786
|
+
page++;
|
|
1787
|
+
if (page > 1e3) {
|
|
1788
|
+
logger.warn(` \u26A0\uFE0F Stopped pagination after 1000 pages (${totalChecked} resources checked)`);
|
|
1789
|
+
return null;
|
|
1790
|
+
}
|
|
1201
1791
|
}
|
|
1202
|
-
return params;
|
|
1203
1792
|
}
|
|
1204
|
-
function
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
settings_config: tool.settings_config ?? Boolean(tool.settings),
|
|
1208
|
-
settings: isResolvedIntegration(tool.settings) ? tool.settings : void 0
|
|
1209
|
-
};
|
|
1793
|
+
async function findDatasourceByName(client, name) {
|
|
1794
|
+
const datasource = await findResourceByName((params) => client.datasources.list(params), name, "datasource");
|
|
1795
|
+
return datasource?.id || null;
|
|
1210
1796
|
}
|
|
1211
|
-
function
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
settings_config: toolkit.settings_config ?? Boolean(toolkit.settings),
|
|
1215
|
-
is_external: toolkit.is_external ?? false,
|
|
1216
|
-
tools: toolkit.tools.map((tool) => iacToolToSdk(tool)),
|
|
1217
|
-
settings: isResolvedIntegration(toolkit.settings) ? toolkit.settings : void 0
|
|
1218
|
-
};
|
|
1797
|
+
async function findAssistantByName(client, name) {
|
|
1798
|
+
const assistant = await findResourceByName((params) => client.assistants.list(params), name, "assistant");
|
|
1799
|
+
return assistant?.id || null;
|
|
1219
1800
|
}
|
|
1220
|
-
function
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
enabled: mcp.enabled ?? true,
|
|
1224
|
-
settings: isResolvedIntegration(mcp.settings) ? mcp.settings : void 0,
|
|
1225
|
-
mcp_connect_auth_token: isResolvedIntegration(mcp.mcp_connect_auth_token) ? mcp.mcp_connect_auth_token : void 0
|
|
1226
|
-
};
|
|
1801
|
+
async function findSkillByName(client, name) {
|
|
1802
|
+
const skill = await findResourceByName((params) => client.skills.list(params), name, "skill");
|
|
1803
|
+
return skill?.id || null;
|
|
1227
1804
|
}
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
name: assistant.name,
|
|
1245
|
-
description: assistant.description,
|
|
1246
|
-
conversation_starters: assistant.conversation_starters || [],
|
|
1247
|
-
toolkits: (toolkits || []).map((toolkit) => iacToolkitToSdk(toolkit)),
|
|
1248
|
-
context: assistant.context || [],
|
|
1249
|
-
mcp_servers: (mcpServers || []).map((mcp) => iacMcpServerToSdk(mcp)),
|
|
1250
|
-
assistant_ids: assistant.assistant_ids || [],
|
|
1251
|
-
shared: assistant.shared ?? true,
|
|
1252
|
-
prompt_variables: assistant.prompt_variables || []
|
|
1253
|
-
};
|
|
1805
|
+
|
|
1806
|
+
// src/lib/assistantHelpers.ts
|
|
1807
|
+
async function createAssistantAndGetId(client, params, slug) {
|
|
1808
|
+
await client.assistants.create(params);
|
|
1809
|
+
if (slug) {
|
|
1810
|
+
const created = await client.assistants.getBySlug(slug);
|
|
1811
|
+
if (!created?.id) {
|
|
1812
|
+
throw new Error(`Assistant created but not found by slug: ${slug}`);
|
|
1813
|
+
}
|
|
1814
|
+
return created.id;
|
|
1815
|
+
}
|
|
1816
|
+
const assistantId = await findAssistantByName(client, params.name);
|
|
1817
|
+
if (!assistantId) {
|
|
1818
|
+
throw new Error(`Assistant created but not found by name: ${params.name}`);
|
|
1819
|
+
}
|
|
1820
|
+
return assistantId;
|
|
1254
1821
|
}
|
|
1255
1822
|
|
|
1823
|
+
// src/deploy.ts
|
|
1824
|
+
init_checksumUtils();
|
|
1825
|
+
init_codemieConfigChecksums();
|
|
1826
|
+
init_converters();
|
|
1827
|
+
init_logger();
|
|
1828
|
+
|
|
1256
1829
|
// src/lib/resourceExistenceChecker.ts
|
|
1257
1830
|
async function checkResourceExists(getState, getResource) {
|
|
1258
1831
|
const existingState = getState();
|
|
@@ -1284,6 +1857,12 @@ function checkWorkflowExists(client, name, stateManager) {
|
|
|
1284
1857
|
(id) => client.workflows.get(id)
|
|
1285
1858
|
);
|
|
1286
1859
|
}
|
|
1860
|
+
function checkSkillExists(client, name, stateManager) {
|
|
1861
|
+
return checkResourceExists(
|
|
1862
|
+
() => stateManager.getSkillState(name),
|
|
1863
|
+
(id) => client.skills.get(id)
|
|
1864
|
+
);
|
|
1865
|
+
}
|
|
1287
1866
|
|
|
1288
1867
|
// src/deploy.ts
|
|
1289
1868
|
function sortAssistantsByDependencies(assistants) {
|
|
@@ -1375,69 +1954,268 @@ async function deployAssistants(config, client, loader, stateManager) {
|
|
|
1375
1954
|
});
|
|
1376
1955
|
logger.info(` \u2713 Resolved "${dsName}" \u2192 ${datasource.name} (${contextType})`);
|
|
1377
1956
|
}
|
|
1378
|
-
resolvedContext = [...resolvedContext, ...datasourceContextEntries];
|
|
1957
|
+
resolvedContext = [...resolvedContext, ...datasourceContextEntries];
|
|
1958
|
+
}
|
|
1959
|
+
let resolvedSkillIds = [];
|
|
1960
|
+
if (assistant.skills && assistant.skills.length > 0) {
|
|
1961
|
+
logger.info(` Resolving ${assistant.skills.length} skill name(s)...`);
|
|
1962
|
+
const resolvedIds = [];
|
|
1963
|
+
for (const skillName of assistant.skills) {
|
|
1964
|
+
const skillState = stateManager.getSkillState(skillName);
|
|
1965
|
+
if (!skillState) {
|
|
1966
|
+
throw new Error(`Skill "${skillName}" not found in state. Ensure the skill is deployed first.`);
|
|
1967
|
+
}
|
|
1968
|
+
resolvedIds.push(skillState.id);
|
|
1969
|
+
logger.info(` \u2713 Resolved "${skillName}" \u2192 ${skillState.id}`);
|
|
1970
|
+
}
|
|
1971
|
+
resolvedSkillIds = resolvedIds;
|
|
1972
|
+
}
|
|
1973
|
+
const assistantWithResolved = {
|
|
1974
|
+
...assistant,
|
|
1975
|
+
assistant_ids: resolvedAssistantIds,
|
|
1976
|
+
context: resolvedContext,
|
|
1977
|
+
skill_ids: resolvedSkillIds
|
|
1978
|
+
};
|
|
1979
|
+
const apiParams = assistantResourceToCreateParams(assistantWithResolved, config.project.name, promptContent);
|
|
1980
|
+
const existingState = stateManager.getAssistantState(assistant.name);
|
|
1981
|
+
if (existingState) {
|
|
1982
|
+
const existsOnPlatform = await checkAssistantExists(client, assistant.name, stateManager);
|
|
1983
|
+
if (existsOnPlatform) {
|
|
1984
|
+
const hasChanged = existingState.promptChecksum !== calculateChecksum(promptContent) || existingState.configChecksum !== configChecksum;
|
|
1985
|
+
if (hasChanged) {
|
|
1986
|
+
logger.info(` Updating assistant (ID: ${existingState.id})...`);
|
|
1987
|
+
if (process.env.DEBUG_API) {
|
|
1988
|
+
logger.debug("\n=== DEBUG: Update API Params ===");
|
|
1989
|
+
logger.debug(JSON.stringify(apiParams, null, 2));
|
|
1990
|
+
logger.debug("================================\n");
|
|
1991
|
+
}
|
|
1992
|
+
await client.assistants.update(existingState.id, apiParams);
|
|
1993
|
+
logger.info(`\u2713 Updated assistant: ${assistant.name} (${existingState.id})`);
|
|
1994
|
+
stateManager.updateAssistantState(assistant.name, existingState.id, promptContent, assistant, buildConfig);
|
|
1995
|
+
stats.updated++;
|
|
1996
|
+
} else {
|
|
1997
|
+
logger.info(` \u2713 No changes detected (ID: ${existingState.id})`);
|
|
1998
|
+
stats.unchanged++;
|
|
1999
|
+
}
|
|
2000
|
+
} else {
|
|
2001
|
+
logger.info(` \u26A0\uFE0F Assistant ID from state not found on platform, will create new`);
|
|
2002
|
+
logger.info(` Creating new assistant...`);
|
|
2003
|
+
if (process.env.DEBUG_API) {
|
|
2004
|
+
logger.debug("\n=== DEBUG: API Params ===");
|
|
2005
|
+
logger.debug(JSON.stringify(apiParams, null, 2));
|
|
2006
|
+
logger.debug("=========================\n");
|
|
2007
|
+
}
|
|
2008
|
+
let assistantId;
|
|
2009
|
+
try {
|
|
2010
|
+
assistantId = await createAssistantAndGetId(client, apiParams, assistant.slug);
|
|
2011
|
+
logger.info(`\u2713 Created assistant: ${assistant.name} (${assistantId})`);
|
|
2012
|
+
stats.created++;
|
|
2013
|
+
} catch (createError) {
|
|
2014
|
+
logger.warn(` \u26A0\uFE0F Assistant create failed. Checking if it already exists on platform...`);
|
|
2015
|
+
let existingId = null;
|
|
2016
|
+
if (assistant.slug) {
|
|
2017
|
+
try {
|
|
2018
|
+
const existingBySlug = await client.assistants.getBySlug(assistant.slug);
|
|
2019
|
+
existingId = existingBySlug?.id || null;
|
|
2020
|
+
} catch {
|
|
2021
|
+
}
|
|
2022
|
+
}
|
|
2023
|
+
if (!existingId) {
|
|
2024
|
+
existingId = await findAssistantByName(client, assistant.name);
|
|
2025
|
+
}
|
|
2026
|
+
if (!existingId) {
|
|
2027
|
+
throw createError;
|
|
2028
|
+
}
|
|
2029
|
+
logger.info(` Found existing assistant ID: ${existingId}. Updating...`);
|
|
2030
|
+
await client.assistants.update(existingId, apiParams);
|
|
2031
|
+
assistantId = existingId;
|
|
2032
|
+
logger.info(`\u2713 Recovered and updated assistant: ${assistant.name} (${assistantId})`);
|
|
2033
|
+
stats.updated++;
|
|
2034
|
+
}
|
|
2035
|
+
stateManager.updateAssistantState(assistant.name, assistantId, promptContent, assistant, buildConfig);
|
|
2036
|
+
}
|
|
2037
|
+
} else {
|
|
2038
|
+
logger.info(` Creating new assistant...`);
|
|
2039
|
+
if (process.env.DEBUG_API) {
|
|
2040
|
+
logger.debug("\n=== DEBUG: API Params ===");
|
|
2041
|
+
logger.debug(JSON.stringify(apiParams, null, 2));
|
|
2042
|
+
logger.debug("=========================\n");
|
|
2043
|
+
}
|
|
2044
|
+
let assistantId;
|
|
2045
|
+
try {
|
|
2046
|
+
assistantId = await createAssistantAndGetId(client, apiParams, assistant.slug);
|
|
2047
|
+
logger.info(`\u2713 Created assistant: ${assistant.name} (${assistantId})`);
|
|
2048
|
+
stats.created++;
|
|
2049
|
+
} catch (createError) {
|
|
2050
|
+
logger.warn(` \u26A0\uFE0F Assistant create failed. Checking if it already exists on platform...`);
|
|
2051
|
+
let existingId = null;
|
|
2052
|
+
if (assistant.slug) {
|
|
2053
|
+
try {
|
|
2054
|
+
const existingBySlug = await client.assistants.getBySlug(assistant.slug);
|
|
2055
|
+
existingId = existingBySlug?.id || null;
|
|
2056
|
+
} catch {
|
|
2057
|
+
}
|
|
2058
|
+
}
|
|
2059
|
+
if (!existingId) {
|
|
2060
|
+
existingId = await findAssistantByName(client, assistant.name);
|
|
2061
|
+
}
|
|
2062
|
+
if (!existingId) {
|
|
2063
|
+
throw createError;
|
|
2064
|
+
}
|
|
2065
|
+
logger.info(` Found existing assistant ID: ${existingId}. Updating...`);
|
|
2066
|
+
await client.assistants.update(existingId, apiParams);
|
|
2067
|
+
assistantId = existingId;
|
|
2068
|
+
logger.info(`\u2713 Recovered and updated assistant: ${assistant.name} (${assistantId})`);
|
|
2069
|
+
stats.updated++;
|
|
2070
|
+
}
|
|
2071
|
+
stateManager.updateAssistantState(assistant.name, assistantId, promptContent, assistant, buildConfig);
|
|
1379
2072
|
}
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
2073
|
+
logger.info("");
|
|
2074
|
+
} catch (error) {
|
|
2075
|
+
logger.error(` \u274C Failed to deploy ${assistant.name}:`);
|
|
2076
|
+
if (error instanceof Error) {
|
|
2077
|
+
logger.error(` ${error.message}`);
|
|
2078
|
+
logger.debug(` Stack:`, error.stack);
|
|
2079
|
+
if ("statusCode" in error) {
|
|
2080
|
+
const apiError = error;
|
|
2081
|
+
logger.error(` Status: ${apiError.statusCode}`);
|
|
2082
|
+
logger.error(` Data: ${JSON.stringify(apiError.response, null, 2)}`);
|
|
2083
|
+
} else if ("response" in error) {
|
|
2084
|
+
const axiosError = error;
|
|
2085
|
+
logger.error(` Status: ${axiosError.response?.status}`);
|
|
2086
|
+
logger.error(` Data: ${JSON.stringify(axiosError.response?.data, null, 2)}`);
|
|
2087
|
+
}
|
|
2088
|
+
} else {
|
|
2089
|
+
logger.error(` ${String(error)}`);
|
|
2090
|
+
}
|
|
2091
|
+
logger.info("");
|
|
2092
|
+
stats.failed++;
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
return stats;
|
|
2096
|
+
}
|
|
2097
|
+
async function deploySkills(config, client, loader, stateManager) {
|
|
2098
|
+
const stats = { created: 0, updated: 0, unchanged: 0, failed: 0 };
|
|
2099
|
+
logger.info("\u{1F3AF} Processing skills...\n");
|
|
2100
|
+
if (!config.resources.skills) {
|
|
2101
|
+
return stats;
|
|
2102
|
+
}
|
|
2103
|
+
for (const skill of config.resources.skills) {
|
|
2104
|
+
try {
|
|
2105
|
+
logger.info(`Processing: ${skill.name}`);
|
|
2106
|
+
const content = loader.loadSkillContent(skill.file);
|
|
2107
|
+
const contentChecksum = calculateChecksum(content);
|
|
2108
|
+
const configChecksum = calculateSkillConfigChecksum(skill);
|
|
2109
|
+
const visibility = skill.visibility ?? "project";
|
|
2110
|
+
const existingState = stateManager.getSkillState(skill.name);
|
|
1387
2111
|
if (existingState) {
|
|
1388
|
-
const existsOnPlatform = await
|
|
2112
|
+
const existsOnPlatform = await checkSkillExists(client, skill.name, stateManager);
|
|
1389
2113
|
if (existsOnPlatform) {
|
|
1390
|
-
const hasChanged = existingState.
|
|
2114
|
+
const hasChanged = existingState.contentChecksum !== contentChecksum || existingState.configChecksum !== configChecksum;
|
|
1391
2115
|
if (hasChanged) {
|
|
1392
|
-
logger.info(` Updating
|
|
2116
|
+
logger.info(` Updating skill (ID: ${existingState.id})...`);
|
|
1393
2117
|
if (process.env.DEBUG_API) {
|
|
1394
2118
|
logger.debug("\n=== DEBUG: Update API Params ===");
|
|
1395
|
-
logger.debug(
|
|
2119
|
+
logger.debug(
|
|
2120
|
+
JSON.stringify(
|
|
2121
|
+
{ name: skill.name, description: skill.description, categories: skill.categories },
|
|
2122
|
+
null,
|
|
2123
|
+
2
|
|
2124
|
+
)
|
|
2125
|
+
);
|
|
1396
2126
|
logger.debug("================================\n");
|
|
1397
2127
|
}
|
|
1398
|
-
await client.
|
|
1399
|
-
|
|
1400
|
-
|
|
2128
|
+
await client.skills.update(existingState.id, {
|
|
2129
|
+
name: skill.name,
|
|
2130
|
+
description: skill.description,
|
|
2131
|
+
content,
|
|
2132
|
+
categories: skill.categories
|
|
2133
|
+
});
|
|
2134
|
+
logger.info(`\u2713 Updated skill: ${skill.name} (${existingState.id})`);
|
|
2135
|
+
stateManager.updateSkillState(skill.name, existingState.id, content, skill);
|
|
1401
2136
|
stats.updated++;
|
|
1402
2137
|
} else {
|
|
1403
2138
|
logger.info(` \u2713 No changes detected (ID: ${existingState.id})`);
|
|
1404
2139
|
stats.unchanged++;
|
|
1405
2140
|
}
|
|
1406
2141
|
} else {
|
|
1407
|
-
logger.info(` \u26A0\uFE0F
|
|
1408
|
-
logger.info(` Creating new
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
2142
|
+
logger.info(` \u26A0\uFE0F Skill ID from state not found on platform, will create new`);
|
|
2143
|
+
logger.info(` Creating new skill...`);
|
|
2144
|
+
const result = await client.skills.create({
|
|
2145
|
+
name: skill.name,
|
|
2146
|
+
description: skill.description,
|
|
2147
|
+
content,
|
|
2148
|
+
project: config.project.name,
|
|
2149
|
+
visibility,
|
|
2150
|
+
categories: skill.categories
|
|
2151
|
+
});
|
|
2152
|
+
logger.info(`\u2713 Created skill: ${skill.name} (${result.id})`);
|
|
2153
|
+
stateManager.updateSkillState(skill.name, result.id, content, skill);
|
|
1417
2154
|
stats.created++;
|
|
1418
2155
|
}
|
|
1419
2156
|
} else {
|
|
1420
|
-
logger.info(` Creating new
|
|
2157
|
+
logger.info(` Creating new skill...`);
|
|
1421
2158
|
if (process.env.DEBUG_API) {
|
|
1422
2159
|
logger.debug("\n=== DEBUG: API Params ===");
|
|
1423
|
-
logger.debug(
|
|
2160
|
+
logger.debug(
|
|
2161
|
+
JSON.stringify(
|
|
2162
|
+
{ name: skill.name, description: skill.description, visibility, categories: skill.categories },
|
|
2163
|
+
null,
|
|
2164
|
+
2
|
|
2165
|
+
)
|
|
2166
|
+
);
|
|
1424
2167
|
logger.debug("=========================\n");
|
|
1425
2168
|
}
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
2169
|
+
let skillId;
|
|
2170
|
+
try {
|
|
2171
|
+
const result = await client.skills.create({
|
|
2172
|
+
name: skill.name,
|
|
2173
|
+
description: skill.description,
|
|
2174
|
+
content,
|
|
2175
|
+
project: config.project.name,
|
|
2176
|
+
visibility,
|
|
2177
|
+
categories: skill.categories
|
|
2178
|
+
});
|
|
2179
|
+
skillId = result.id;
|
|
2180
|
+
logger.info(`\u2713 Created skill: ${skill.name} (${skillId})`);
|
|
2181
|
+
stats.created++;
|
|
2182
|
+
} catch (createError) {
|
|
2183
|
+
const msg = createError instanceof Error ? createError.message : String(createError);
|
|
2184
|
+
if (!msg.includes("already exists")) {
|
|
2185
|
+
throw createError;
|
|
2186
|
+
}
|
|
2187
|
+
logger.warn(` \u26A0\uFE0F Skill already exists on platform (no local state). Fetching ID to recover...`);
|
|
2188
|
+
const existingId = await findSkillByName(client, skill.name);
|
|
2189
|
+
if (!existingId) {
|
|
2190
|
+
throw new Error(`Skill "${skill.name}" reported as existing but could not be found by name.`);
|
|
2191
|
+
}
|
|
2192
|
+
logger.info(` Found existing skill ID: ${existingId}. Updating...`);
|
|
2193
|
+
await client.skills.update(existingId, {
|
|
2194
|
+
name: skill.name,
|
|
2195
|
+
description: skill.description,
|
|
2196
|
+
content,
|
|
2197
|
+
categories: skill.categories
|
|
2198
|
+
});
|
|
2199
|
+
skillId = existingId;
|
|
2200
|
+
logger.info(`\u2713 Recovered and updated skill: ${skill.name} (${skillId})`);
|
|
2201
|
+
stats.updated++;
|
|
2202
|
+
}
|
|
2203
|
+
stateManager.updateSkillState(skill.name, skillId, content, skill);
|
|
1430
2204
|
}
|
|
1431
2205
|
logger.info("");
|
|
1432
2206
|
} catch (error) {
|
|
1433
|
-
logger.error(` \u274C Failed to deploy ${
|
|
2207
|
+
logger.error(` \u274C Failed to deploy ${skill.name}:`);
|
|
1434
2208
|
if (error instanceof Error) {
|
|
1435
2209
|
logger.error(` ${error.message}`);
|
|
1436
2210
|
logger.debug(` Stack:`, error.stack);
|
|
1437
|
-
if ("
|
|
2211
|
+
if ("statusCode" in error) {
|
|
2212
|
+
const apiError = error;
|
|
2213
|
+
logger.error(` Status: ${apiError.statusCode}`);
|
|
2214
|
+
logger.error(` Data: ${JSON.stringify(apiError.response, null, 2)}`);
|
|
2215
|
+
} else if ("response" in error) {
|
|
1438
2216
|
const axiosError = error;
|
|
1439
2217
|
logger.error(` Status: ${axiosError.response?.status}`);
|
|
1440
|
-
logger.error(` Data
|
|
2218
|
+
logger.error(` Data: ${JSON.stringify(axiosError.response?.data, null, 2)}`);
|
|
1441
2219
|
}
|
|
1442
2220
|
} else {
|
|
1443
2221
|
logger.error(` ${String(error)}`);
|
|
@@ -1459,6 +2237,24 @@ async function createDatasourceAndGetId(client, createParams, datasourceName) {
|
|
|
1459
2237
|
logger.info(` Found ID: ${datasourceId}`);
|
|
1460
2238
|
return datasourceId;
|
|
1461
2239
|
}
|
|
2240
|
+
async function createOrRecoverDatasource(client, createParams, datasourceName) {
|
|
2241
|
+
try {
|
|
2242
|
+
return await createDatasourceAndGetId(client, createParams, datasourceName);
|
|
2243
|
+
} catch (createError) {
|
|
2244
|
+
if (createError instanceof Error && createError.message.toLowerCase().includes("already exists")) {
|
|
2245
|
+
logger.warn(` \u26A0\uFE0F Datasource "${datasourceName}" already exists on platform. Recovering existing ID...`);
|
|
2246
|
+
const existingId = await findDatasourceByName(client, datasourceName);
|
|
2247
|
+
if (!existingId) {
|
|
2248
|
+
throw new Error(
|
|
2249
|
+
`Datasource "${datasourceName}" already exists but could not be found by name. Manual intervention required.`
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
logger.info(` Found existing ID: ${existingId}`);
|
|
2253
|
+
return existingId;
|
|
2254
|
+
}
|
|
2255
|
+
throw createError;
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
1462
2258
|
async function deployDatasources(config, client, stateManager) {
|
|
1463
2259
|
const stats = { created: 0, updated: 0, unchanged: 0, failed: 0 };
|
|
1464
2260
|
logger.info("\u{1F4CA} Processing datasources...\n");
|
|
@@ -1485,12 +2281,24 @@ async function deployDatasources(config, client, stateManager) {
|
|
|
1485
2281
|
...createParams,
|
|
1486
2282
|
...datasource.force_reindex && { full_reindex: true }
|
|
1487
2283
|
};
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
2284
|
+
try {
|
|
2285
|
+
await client.datasources.update(updateParams);
|
|
2286
|
+
logger.info(
|
|
2287
|
+
`\u2713 ${datasource.force_reindex && !hasChanged ? "Reindexed" : "Updated"} datasource: ${datasource.name} (${existingState.id})`
|
|
2288
|
+
);
|
|
2289
|
+
stateManager.updateDatasourceState(datasource.name, existingState.id, datasource);
|
|
2290
|
+
stats.updated++;
|
|
2291
|
+
} catch (updateError) {
|
|
2292
|
+
if (updateError instanceof Error && updateError.message.includes("unknown not found")) {
|
|
2293
|
+
logger.warn(
|
|
2294
|
+
` \u26A0\uFE0F Datasource "${datasource.name}" update returned 404 \u2014 likely externally managed and read-only. Skipping update.`
|
|
2295
|
+
);
|
|
2296
|
+
stateManager.updateDatasourceState(datasource.name, existingState.id, datasource);
|
|
2297
|
+
stats.unchanged++;
|
|
2298
|
+
} else {
|
|
2299
|
+
throw updateError;
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
1494
2302
|
} else {
|
|
1495
2303
|
logger.info(` \u2713 No changes detected (ID: ${existingState.id})`);
|
|
1496
2304
|
stats.unchanged++;
|
|
@@ -1498,13 +2306,13 @@ async function deployDatasources(config, client, stateManager) {
|
|
|
1498
2306
|
} else {
|
|
1499
2307
|
logger.info(` \u26A0\uFE0F Datasource ID from state not found on platform, will create new`);
|
|
1500
2308
|
logger.info(` Creating new datasource...`);
|
|
1501
|
-
const datasourceId = await
|
|
2309
|
+
const datasourceId = await createOrRecoverDatasource(client, createParams, datasource.name);
|
|
1502
2310
|
stateManager.updateDatasourceState(datasource.name, datasourceId, datasource);
|
|
1503
2311
|
stats.created++;
|
|
1504
2312
|
}
|
|
1505
2313
|
} else {
|
|
1506
2314
|
logger.info(` Creating new datasource...`);
|
|
1507
|
-
const datasourceId = await
|
|
2315
|
+
const datasourceId = await createOrRecoverDatasource(client, createParams, datasource.name);
|
|
1508
2316
|
stateManager.updateDatasourceState(datasource.name, datasourceId, datasource);
|
|
1509
2317
|
stats.created++;
|
|
1510
2318
|
}
|
|
@@ -1542,7 +2350,7 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
|
|
|
1542
2350
|
throw new Error(`Workflow definition file not found: ${workflow.definition}`);
|
|
1543
2351
|
}
|
|
1544
2352
|
const yamlConfigContent = fs.readFileSync(yamlPath, "utf8");
|
|
1545
|
-
const workflowYaml =
|
|
2353
|
+
const workflowYaml = yaml6.parse(yamlConfigContent);
|
|
1546
2354
|
const referencesToResolve = [];
|
|
1547
2355
|
for (const assistant of workflowYaml.assistants) {
|
|
1548
2356
|
const isInline = !assistant.assistant_name && assistant.model && assistant.system_prompt;
|
|
@@ -1583,7 +2391,7 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
|
|
|
1583
2391
|
});
|
|
1584
2392
|
}
|
|
1585
2393
|
}
|
|
1586
|
-
const yamlConfig =
|
|
2394
|
+
const yamlConfig = yaml6.stringify(workflowYaml);
|
|
1587
2395
|
const workflowYamlChecksum = calculateChecksum(yamlConfigContent);
|
|
1588
2396
|
const configChecksum = calculateWorkflowConfigChecksum(workflow);
|
|
1589
2397
|
const existingState = stateManager.getWorkflowState(workflow.name);
|
|
@@ -1702,7 +2510,7 @@ async function deployResources(options) {
|
|
|
1702
2510
|
logger.info("\u{1F9F9} Checking for orphaned resources...");
|
|
1703
2511
|
const cleanupManager = new CleanupManager(client, stateManager);
|
|
1704
2512
|
const orphaned = cleanupManager.findOrphanedResources(config);
|
|
1705
|
-
const totalOrphaned = orphaned.assistants.length + orphaned.datasources.length + orphaned.workflows.length;
|
|
2513
|
+
const totalOrphaned = orphaned.assistants.length + orphaned.datasources.length + orphaned.workflows.length + orphaned.skills.length;
|
|
1706
2514
|
let deleted = 0;
|
|
1707
2515
|
if (totalOrphaned > 0) {
|
|
1708
2516
|
logger.info(`
|
|
@@ -1716,12 +2524,15 @@ async function deployResources(options) {
|
|
|
1716
2524
|
if (orphaned.workflows.length > 0) {
|
|
1717
2525
|
logger.info(` \u2022 ${orphaned.workflows.length} workflow(s)`);
|
|
1718
2526
|
}
|
|
2527
|
+
if (orphaned.skills.length > 0) {
|
|
2528
|
+
logger.info(` \u2022 ${orphaned.skills.length} skill(s)`);
|
|
2529
|
+
}
|
|
1719
2530
|
if (process.env.SAMPLE_DEPLOY === "1") {
|
|
1720
2531
|
logger.info("\n\u{1F50E} SAMPLE_DEPLOY=1 -> Skipping orphan deletion (simulation / partial deploy mode)\n");
|
|
1721
2532
|
} else if (prune) {
|
|
1722
2533
|
logger.info("\n\u{1F5D1}\uFE0F Deleting orphaned resources (removed from config)...\n");
|
|
1723
2534
|
const cleanupResult = await cleanupManager.deleteOrphanedResources(orphaned);
|
|
1724
|
-
deleted = cleanupResult.deleted.assistants.length + cleanupResult.deleted.datasources.length + cleanupResult.deleted.workflows.length;
|
|
2535
|
+
deleted = cleanupResult.deleted.assistants.length + cleanupResult.deleted.datasources.length + cleanupResult.deleted.workflows.length + cleanupResult.deleted.skills.length;
|
|
1725
2536
|
if (deleted > 0) {
|
|
1726
2537
|
logger.info(`
|
|
1727
2538
|
\u2713 Deleted ${deleted} orphaned resource(s)
|
|
@@ -1748,6 +2559,11 @@ async function deployResources(options) {
|
|
|
1748
2559
|
updated += datasourceStats.updated;
|
|
1749
2560
|
unchanged += datasourceStats.unchanged;
|
|
1750
2561
|
failed += datasourceStats.failed;
|
|
2562
|
+
const skillStats = await deploySkills(config, client, loader, stateManager);
|
|
2563
|
+
created += skillStats.created;
|
|
2564
|
+
updated += skillStats.updated;
|
|
2565
|
+
unchanged += skillStats.unchanged;
|
|
2566
|
+
failed += skillStats.failed;
|
|
1751
2567
|
const assistantStats = await deployAssistants(config, client, loader, stateManager);
|
|
1752
2568
|
created += assistantStats.created;
|
|
1753
2569
|
updated += assistantStats.updated;
|
|
@@ -1804,10 +2620,10 @@ var DependencyValidator = class {
|
|
|
1804
2620
|
const errors = [];
|
|
1805
2621
|
const visited = /* @__PURE__ */ new Set();
|
|
1806
2622
|
const recursionStack = /* @__PURE__ */ new Set();
|
|
1807
|
-
const dfs = (name,
|
|
2623
|
+
const dfs = (name, path12) => {
|
|
1808
2624
|
if (recursionStack.has(name)) {
|
|
1809
|
-
const cycleStart =
|
|
1810
|
-
const cycle = [...
|
|
2625
|
+
const cycleStart = path12.indexOf(name);
|
|
2626
|
+
const cycle = [...path12.slice(cycleStart), name];
|
|
1811
2627
|
errors.push(`Cyclic dependency detected: ${cycle.join(" \u2192 ")}`);
|
|
1812
2628
|
return true;
|
|
1813
2629
|
}
|
|
@@ -1816,14 +2632,14 @@ var DependencyValidator = class {
|
|
|
1816
2632
|
}
|
|
1817
2633
|
visited.add(name);
|
|
1818
2634
|
recursionStack.add(name);
|
|
1819
|
-
|
|
2635
|
+
path12.push(name);
|
|
1820
2636
|
const assistant = assistantMap.get(name);
|
|
1821
2637
|
const subAssistantRefs = assistant?.sub_assistants || [];
|
|
1822
2638
|
for (const subRef of subAssistantRefs) {
|
|
1823
2639
|
if (!assistantMap.has(subRef)) {
|
|
1824
2640
|
continue;
|
|
1825
2641
|
}
|
|
1826
|
-
if (dfs(subRef, [...
|
|
2642
|
+
if (dfs(subRef, [...path12])) {
|
|
1827
2643
|
return true;
|
|
1828
2644
|
}
|
|
1829
2645
|
}
|
|
@@ -1882,7 +2698,7 @@ var DependencyValidator = class {
|
|
|
1882
2698
|
}
|
|
1883
2699
|
for (const workflow of workflows) {
|
|
1884
2700
|
try {
|
|
1885
|
-
const workflowYaml =
|
|
2701
|
+
const workflowYaml = yaml6.parse(workflow.definition);
|
|
1886
2702
|
if (!workflowYaml.assistants) {
|
|
1887
2703
|
continue;
|
|
1888
2704
|
}
|
|
@@ -1906,6 +2722,9 @@ var DependencyValidator = class {
|
|
|
1906
2722
|
}
|
|
1907
2723
|
};
|
|
1908
2724
|
|
|
2725
|
+
// src/validate.ts
|
|
2726
|
+
init_logger();
|
|
2727
|
+
|
|
1909
2728
|
// src/lib/validationUtils.ts
|
|
1910
2729
|
function isAssistantWithSlug(obj) {
|
|
1911
2730
|
return typeof obj === "object" && obj !== null && "slug" in obj && "name" in obj && typeof obj.slug === "string" && typeof obj.name === "string";
|
|
@@ -2011,12 +2830,18 @@ async function validateConfig(options) {
|
|
|
2011
2830
|
}
|
|
2012
2831
|
return { success: errors.length === 0, errors };
|
|
2013
2832
|
}
|
|
2833
|
+
init_logger();
|
|
2834
|
+
|
|
2835
|
+
// src/lib/previewUtils.ts
|
|
2836
|
+
init_checksumUtils();
|
|
2837
|
+
init_codemieConfigChecksums();
|
|
2838
|
+
init_converters();
|
|
2014
2839
|
async function previewResource(resource, resourceType, getState, checkExists, calculateChecksums) {
|
|
2015
2840
|
const existingState = getState(resource.name);
|
|
2016
2841
|
if (existingState) {
|
|
2017
2842
|
const existsOnPlatform = await checkExists();
|
|
2018
2843
|
if (existsOnPlatform) {
|
|
2019
|
-
const { hasChanged, updateDetails } = calculateChecksums();
|
|
2844
|
+
const { hasChanged, updateDetails } = await calculateChecksums();
|
|
2020
2845
|
return hasChanged ? {
|
|
2021
2846
|
type: "update",
|
|
2022
2847
|
resourceType,
|
|
@@ -2036,7 +2861,7 @@ async function previewResource(resource, resourceType, getState, checkExists, ca
|
|
|
2036
2861
|
};
|
|
2037
2862
|
}
|
|
2038
2863
|
} else {
|
|
2039
|
-
const { createDetails } = calculateChecksums();
|
|
2864
|
+
const { createDetails } = await calculateChecksums();
|
|
2040
2865
|
return {
|
|
2041
2866
|
type: "create",
|
|
2042
2867
|
resourceType,
|
|
@@ -2074,6 +2899,42 @@ function previewOrphanedResources(orphaned, stateManager) {
|
|
|
2074
2899
|
details: workflowState ? `Removed from config (ID: ${workflowState.id})` : "Removed from config"
|
|
2075
2900
|
});
|
|
2076
2901
|
}
|
|
2902
|
+
for (const name of orphaned.skills || []) {
|
|
2903
|
+
const skillState = stateManager.getSkillState(name);
|
|
2904
|
+
changes.push({
|
|
2905
|
+
type: "delete",
|
|
2906
|
+
resourceType: "skill",
|
|
2907
|
+
name,
|
|
2908
|
+
details: skillState ? `Removed from config (ID: ${skillState.id})` : "Removed from config"
|
|
2909
|
+
});
|
|
2910
|
+
}
|
|
2911
|
+
return changes;
|
|
2912
|
+
}
|
|
2913
|
+
async function previewSkills(skills, loader, stateManager, client) {
|
|
2914
|
+
const changes = [];
|
|
2915
|
+
for (const skill of skills) {
|
|
2916
|
+
const content = loader.loadSkillContent(skill.file);
|
|
2917
|
+
const contentChecksum = calculateChecksum(content);
|
|
2918
|
+
const configChecksum = calculateSkillConfigChecksum(skill);
|
|
2919
|
+
const change = await previewResource(
|
|
2920
|
+
skill,
|
|
2921
|
+
"skill",
|
|
2922
|
+
(name) => stateManager.getSkillState(name),
|
|
2923
|
+
() => checkSkillExists(client, skill.name, stateManager),
|
|
2924
|
+
() => {
|
|
2925
|
+
const existingState = stateManager.getSkillState(skill.name);
|
|
2926
|
+
const hasChanged = existingState ? existingState.contentChecksum !== contentChecksum || existingState.configChecksum !== configChecksum : false;
|
|
2927
|
+
return {
|
|
2928
|
+
hasChanged,
|
|
2929
|
+
updateDetails: "Content or configuration changed"
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
);
|
|
2933
|
+
if (!change.details && change.type === "create") {
|
|
2934
|
+
change.details = skill.visibility === "public" ? "Visibility: public" : "Visibility: project";
|
|
2935
|
+
}
|
|
2936
|
+
changes.push(change);
|
|
2937
|
+
}
|
|
2077
2938
|
return changes;
|
|
2078
2939
|
}
|
|
2079
2940
|
async function previewAssistants(assistants, loader, stateManager, client) {
|
|
@@ -2112,10 +2973,19 @@ async function previewDatasources(datasources, stateManager, client) {
|
|
|
2112
2973
|
"datasource",
|
|
2113
2974
|
(name) => stateManager.getDatasourceState(name),
|
|
2114
2975
|
() => checkDatasourceExists(client, datasource.name, stateManager),
|
|
2115
|
-
() => {
|
|
2976
|
+
async () => {
|
|
2116
2977
|
const configChecksum = calculateDatasourceConfigChecksum(datasource);
|
|
2117
2978
|
const existingState = stateManager.getDatasourceState(datasource.name);
|
|
2118
|
-
|
|
2979
|
+
let hasChanged = existingState ? existingState.configChecksum !== configChecksum : false;
|
|
2980
|
+
if (hasChanged && existingState?.id) {
|
|
2981
|
+
try {
|
|
2982
|
+
const datasourceOnPlatform = await client.datasources.get(existingState.id);
|
|
2983
|
+
const normalizedPlatformDatasource = datasourceResponseToResource(datasourceOnPlatform);
|
|
2984
|
+
const platformChecksum = calculateDatasourceConfigChecksum(normalizedPlatformDatasource);
|
|
2985
|
+
hasChanged = platformChecksum !== configChecksum;
|
|
2986
|
+
} catch {
|
|
2987
|
+
}
|
|
2988
|
+
}
|
|
2119
2989
|
if (hasChanged || datasource.force_reindex) {
|
|
2120
2990
|
return {
|
|
2121
2991
|
hasChanged: true,
|
|
@@ -2174,6 +3044,10 @@ async function previewChanges(appConfig, existingClient) {
|
|
|
2174
3044
|
const cleanupManager = new CleanupManager(client, stateManager);
|
|
2175
3045
|
const orphaned = cleanupManager.findOrphanedResources(config);
|
|
2176
3046
|
changes.push(...previewOrphanedResources(orphaned, stateManager));
|
|
3047
|
+
if (config.resources.skills) {
|
|
3048
|
+
const skillChanges = await previewSkills(config.resources.skills, loader, stateManager, client);
|
|
3049
|
+
changes.push(...skillChanges);
|
|
3050
|
+
}
|
|
2177
3051
|
if (config.resources.assistants) {
|
|
2178
3052
|
const assistantChanges = await previewAssistants(config.resources.assistants, loader, stateManager, client);
|
|
2179
3053
|
changes.push(...assistantChanges);
|
|
@@ -2209,7 +3083,8 @@ var TIMEOUTS_MS = {
|
|
|
2209
3083
|
ASSISTANT_FETCH: 3e4,
|
|
2210
3084
|
DATASOURCE_FETCH: 3e4,
|
|
2211
3085
|
WORKFLOW_FETCH: 3e4,
|
|
2212
|
-
INTEGRATION_FETCH: 3e4
|
|
3086
|
+
INTEGRATION_FETCH: 3e4,
|
|
3087
|
+
SKILL_FETCH: 3e4
|
|
2213
3088
|
};
|
|
2214
3089
|
var RATE_LIMITING = {
|
|
2215
3090
|
MAX_CONCURRENT_REQUESTS: 5,
|
|
@@ -2220,63 +3095,10 @@ var BACKUP = {
|
|
|
2220
3095
|
TEMP_DIR_PREFIX: ".temp-",
|
|
2221
3096
|
TRANSACTION_SAVE_TIMEOUT_MS: 5e3
|
|
2222
3097
|
};
|
|
2223
|
-
var BYTES_IN_GB = 1024 ** 3;
|
|
2224
|
-
function sanitizeFileName(name, maxLength = 255) {
|
|
2225
|
-
const nameWithHyphens = name.replaceAll(/[/\\]/g, "-");
|
|
2226
|
-
const sanitized = nameWithHyphens.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replaceAll(/^-+|-+$/g, "").slice(0, maxLength);
|
|
2227
|
-
if (!sanitized) {
|
|
2228
|
-
throw new Error(`Sanitized filename is empty for input: "${name}"`);
|
|
2229
|
-
}
|
|
2230
|
-
return sanitized;
|
|
2231
|
-
}
|
|
2232
|
-
function validateBackupDirectory(backupDir, minSpaceGB = 1) {
|
|
2233
|
-
try {
|
|
2234
|
-
const parentDir = path.dirname(backupDir);
|
|
2235
|
-
if (!fs.existsSync(parentDir)) {
|
|
2236
|
-
fs.mkdirSync(parentDir, { recursive: true });
|
|
2237
|
-
}
|
|
2238
|
-
const testFile = path.join(parentDir, ".write-test");
|
|
2239
|
-
fs.writeFileSync(testFile, "test");
|
|
2240
|
-
fs.unlinkSync(testFile);
|
|
2241
|
-
try {
|
|
2242
|
-
const stats = fs.statfsSync(parentDir);
|
|
2243
|
-
const availableGB = stats.bavail * stats.bsize / BYTES_IN_GB;
|
|
2244
|
-
if (availableGB < minSpaceGB) {
|
|
2245
|
-
throw new Error(`Insufficient disk space: ${availableGB.toFixed(2)}GB available, need ${minSpaceGB}GB`);
|
|
2246
|
-
}
|
|
2247
|
-
} catch (error) {
|
|
2248
|
-
if (error.code !== "ERR_METHOD_NOT_SUPPORTED") {
|
|
2249
|
-
throw error;
|
|
2250
|
-
}
|
|
2251
|
-
}
|
|
2252
|
-
} catch (error) {
|
|
2253
|
-
throw new Error(`Cannot write to backup directory: ${error instanceof Error ? error.message : String(error)}`);
|
|
2254
|
-
}
|
|
2255
|
-
}
|
|
2256
|
-
function moveAtomically(tempPath, finalPath) {
|
|
2257
|
-
try {
|
|
2258
|
-
fs.renameSync(tempPath, finalPath);
|
|
2259
|
-
} catch (error) {
|
|
2260
|
-
const err = error;
|
|
2261
|
-
if (err.code === "EEXIST") {
|
|
2262
|
-
throw new Error(`Destination already exists: ${finalPath}`);
|
|
2263
|
-
}
|
|
2264
|
-
throw error;
|
|
2265
|
-
}
|
|
2266
|
-
}
|
|
2267
|
-
function cleanupDirectory(dirPath) {
|
|
2268
|
-
if (fs.existsSync(dirPath)) {
|
|
2269
|
-
fs.rmSync(dirPath, { recursive: true, force: true });
|
|
2270
|
-
}
|
|
2271
|
-
}
|
|
2272
|
-
function ensureDirectoryExists(filePath) {
|
|
2273
|
-
const dir = path.dirname(filePath);
|
|
2274
|
-
if (!fs.existsSync(dir)) {
|
|
2275
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
2276
|
-
}
|
|
2277
|
-
}
|
|
2278
3098
|
|
|
2279
3099
|
// src/lib/backupTransaction.ts
|
|
3100
|
+
init_fileUtils();
|
|
3101
|
+
init_logger();
|
|
2280
3102
|
function busyWaitDelay(ms) {
|
|
2281
3103
|
}
|
|
2282
3104
|
var BackupTransaction = class {
|
|
@@ -2309,7 +3131,8 @@ var BackupTransaction = class {
|
|
|
2309
3131
|
assistants: { total: 0, completed: [], failed: [] },
|
|
2310
3132
|
datasources: { total: 0, completed: [], failed: [] },
|
|
2311
3133
|
workflows: { total: 0, completed: [], failed: [] },
|
|
2312
|
-
integrations: { total: 0, completed: [], failed: [] }
|
|
3134
|
+
integrations: { total: 0, completed: [], failed: [] },
|
|
3135
|
+
skills: { total: 0, completed: [], failed: [] }
|
|
2313
3136
|
}
|
|
2314
3137
|
};
|
|
2315
3138
|
ensureDirectoryExists(this.transactionPath);
|
|
@@ -2463,201 +3286,58 @@ var BackupTransaction = class {
|
|
|
2463
3286
|
return this.data.resources[resourceType].completed.includes(resourceId);
|
|
2464
3287
|
}
|
|
2465
3288
|
/**
|
|
2466
|
-
* Mark transaction as completed
|
|
2467
|
-
*/
|
|
2468
|
-
complete() {
|
|
2469
|
-
this.end("completed");
|
|
2470
|
-
}
|
|
2471
|
-
/**
|
|
2472
|
-
* Mark transaction as failed
|
|
2473
|
-
*/
|
|
2474
|
-
fail() {
|
|
2475
|
-
this.end("failed");
|
|
2476
|
-
}
|
|
2477
|
-
/**
|
|
2478
|
-
* Get transaction data (returns deep copy to prevent external modifications)
|
|
2479
|
-
*/
|
|
2480
|
-
getData() {
|
|
2481
|
-
return structuredClone(this.data);
|
|
2482
|
-
}
|
|
2483
|
-
/**
|
|
2484
|
-
* Get summary of backup progress
|
|
2485
|
-
*/
|
|
2486
|
-
getSummary() {
|
|
2487
|
-
const { resources } = this.data;
|
|
2488
|
-
const lines = [];
|
|
2489
|
-
for (const [type, data] of Object.entries(resources)) {
|
|
2490
|
-
const completed = data.completed.length;
|
|
2491
|
-
const failed = data.failed.length;
|
|
2492
|
-
const total = data.total;
|
|
2493
|
-
const percent = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
2494
|
-
lines.push(` ${type}: ${completed}/${total} (${percent}%) ${failed > 0 ? `[${failed} failed]` : ""}`);
|
|
2495
|
-
}
|
|
2496
|
-
return lines.join("\n");
|
|
2497
|
-
}
|
|
2498
|
-
/**
|
|
2499
|
-
* Clean up transaction file after successful completion
|
|
2500
|
-
*/
|
|
2501
|
-
cleanup() {
|
|
2502
|
-
if (fs.existsSync(this.transactionPath)) {
|
|
2503
|
-
fs.unlinkSync(this.transactionPath);
|
|
2504
|
-
}
|
|
2505
|
-
}
|
|
2506
|
-
};
|
|
2507
|
-
function transformIntegrationSettings(settings, integrationIdToAlias, integrationSpecPaths, contextLabel = "integration") {
|
|
2508
|
-
const integrationId = settings.id;
|
|
2509
|
-
const alias = settings.alias || integrationId && integrationIdToAlias.get(integrationId);
|
|
2510
|
-
if (alias) {
|
|
2511
|
-
return { $ref: `imported.integrations.${alias}` };
|
|
2512
|
-
}
|
|
2513
|
-
if (integrationId) {
|
|
2514
|
-
logger.warn(` \u26A0\uFE0F No alias found for ${contextLabel} ${integrationId}, keeping full settings`);
|
|
2515
|
-
const specPath = integrationSpecPaths?.get(integrationId);
|
|
2516
|
-
if (specPath && settings.credential_values) {
|
|
2517
|
-
return {
|
|
2518
|
-
...settings,
|
|
2519
|
-
credential_values: settings.credential_values.map(
|
|
2520
|
-
(cred) => cred.key === "openapi_spec" ? { key: "openapi_spec", value: specPath } : cred
|
|
2521
|
-
)
|
|
2522
|
-
};
|
|
2523
|
-
}
|
|
2524
|
-
}
|
|
2525
|
-
return settings;
|
|
2526
|
-
}
|
|
2527
|
-
function transformTool(tool, integrationIdToAlias, integrationSpecPaths) {
|
|
2528
|
-
const result = {
|
|
2529
|
-
name: tool.name,
|
|
2530
|
-
settings_config: tool.settings_config
|
|
2531
|
-
};
|
|
2532
|
-
if (tool.label !== void 0 && tool.label !== null) {
|
|
2533
|
-
result.label = tool.label;
|
|
2534
|
-
}
|
|
2535
|
-
if (tool.settings) {
|
|
2536
|
-
result.settings = transformIntegrationSettings(tool.settings, integrationIdToAlias, integrationSpecPaths, "tool");
|
|
2537
|
-
}
|
|
2538
|
-
return result;
|
|
2539
|
-
}
|
|
2540
|
-
function transformMcpServer(mcp, integrationIdToAlias) {
|
|
2541
|
-
const result = {
|
|
2542
|
-
name: mcp.name,
|
|
2543
|
-
description: mcp.description,
|
|
2544
|
-
enabled: mcp.enabled,
|
|
2545
|
-
command: mcp.command,
|
|
2546
|
-
arguments: mcp.arguments,
|
|
2547
|
-
config: mcp.config,
|
|
2548
|
-
mcp_connect_url: mcp.mcp_connect_url,
|
|
2549
|
-
mcp_connect_auth_token: mcp.mcp_connect_auth_token,
|
|
2550
|
-
tools_tokens_size_limit: mcp.tools_tokens_size_limit
|
|
2551
|
-
};
|
|
2552
|
-
if (mcp.settings && isResolvedIntegration(mcp.settings)) {
|
|
2553
|
-
const alias = mcp.settings.alias || integrationIdToAlias.get(mcp.settings.id);
|
|
2554
|
-
if (alias) {
|
|
2555
|
-
result.settings = { $ref: `imported.integrations.${alias}` };
|
|
2556
|
-
} else {
|
|
2557
|
-
logger.warn(` \u26A0\uFE0F No alias found for MCP integration ${mcp.settings.id}, keeping full settings`);
|
|
2558
|
-
result.settings = mcp.settings;
|
|
2559
|
-
}
|
|
2560
|
-
} else if (mcp.settings) {
|
|
2561
|
-
result.settings = mcp.settings;
|
|
3289
|
+
* Mark transaction as completed
|
|
3290
|
+
*/
|
|
3291
|
+
complete() {
|
|
3292
|
+
this.end("completed");
|
|
2562
3293
|
}
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
3294
|
+
/**
|
|
3295
|
+
* Mark transaction as failed
|
|
3296
|
+
*/
|
|
3297
|
+
fail() {
|
|
3298
|
+
this.end("failed");
|
|
3299
|
+
}
|
|
3300
|
+
/**
|
|
3301
|
+
* Get transaction data (returns deep copy to prevent external modifications)
|
|
3302
|
+
*/
|
|
3303
|
+
getData() {
|
|
3304
|
+
return structuredClone(this.data);
|
|
3305
|
+
}
|
|
3306
|
+
/**
|
|
3307
|
+
* Get summary of backup progress
|
|
3308
|
+
*/
|
|
3309
|
+
getSummary() {
|
|
3310
|
+
const { resources } = this.data;
|
|
3311
|
+
const lines = [];
|
|
3312
|
+
for (const [type, data] of Object.entries(resources)) {
|
|
3313
|
+
const completed = data.completed.length;
|
|
3314
|
+
const failed = data.failed.length;
|
|
3315
|
+
const total = data.total;
|
|
3316
|
+
const percent = total > 0 ? Math.round(completed / total * 100) : 0;
|
|
3317
|
+
lines.push(` ${type}: ${completed}/${total} (${percent}%) ${failed > 0 ? `[${failed} failed]` : ""}`);
|
|
2571
3318
|
}
|
|
3319
|
+
return lines.join("\n");
|
|
2572
3320
|
}
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
label,
|
|
2580
|
-
settings_config,
|
|
2581
|
-
is_external,
|
|
2582
|
-
...settings ? {
|
|
2583
|
-
settings: transformIntegrationSettings(settings, integrationIdToAlias, integrationSpecPaths, "toolkit")
|
|
2584
|
-
} : {}
|
|
2585
|
-
}));
|
|
2586
|
-
}
|
|
2587
|
-
function prepareAssistantForYaml(assistant, state, integrationIdToAlias, integrationSpecPaths) {
|
|
2588
|
-
const transformedToolkits = transformToolkits(assistant.toolkits, integrationIdToAlias, integrationSpecPaths);
|
|
2589
|
-
const transformedMcpServers = assistant.mcp_servers?.map((mcp) => transformMcpServer(mcp, integrationIdToAlias));
|
|
2590
|
-
const finalAssistant = {
|
|
2591
|
-
...assistantResponseToResource(assistant),
|
|
2592
|
-
...transformedToolkits && { toolkits: transformedToolkits },
|
|
2593
|
-
...transformedMcpServers && { mcp_servers: transformedMcpServers }
|
|
2594
|
-
};
|
|
2595
|
-
state.resources.assistants[assistant.name] = {
|
|
2596
|
-
id: assistant.id,
|
|
2597
|
-
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2598
|
-
promptChecksum: calculateChecksum(assistant.system_prompt || ""),
|
|
2599
|
-
configChecksum: calculateAssistantConfigChecksum(finalAssistant)
|
|
2600
|
-
};
|
|
2601
|
-
return finalAssistant;
|
|
2602
|
-
}
|
|
2603
|
-
function prepareDatasourceForYaml(datasource, state, integrationIdToAlias) {
|
|
2604
|
-
const settingId = hasSettingId(datasource) ? String(datasource.setting_id || "") : "";
|
|
2605
|
-
const integrationAlias = integrationIdToAlias.get(settingId);
|
|
2606
|
-
const finalDatasource = datasourceResponseToResource(datasource, integrationAlias);
|
|
2607
|
-
state.resources.datasources[datasource.name] = {
|
|
2608
|
-
id: datasource.id,
|
|
2609
|
-
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2610
|
-
configChecksum: calculateDatasourceConfigChecksum(finalDatasource)
|
|
2611
|
-
};
|
|
2612
|
-
return finalDatasource;
|
|
2613
|
-
}
|
|
2614
|
-
function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
|
|
2615
|
-
const resource = workflowResponseToResource(workflow);
|
|
2616
|
-
const yamlConfig = workflow.yaml_config;
|
|
2617
|
-
let finalYamlContent = yamlConfig || "";
|
|
2618
|
-
if (yamlConfig) {
|
|
2619
|
-
try {
|
|
2620
|
-
const workflowYaml = yaml.parse(yamlConfig);
|
|
2621
|
-
const transformedAssistants = workflowYaml.assistants?.map((assistant) => {
|
|
2622
|
-
if (assistant.assistant_id && typeof assistant.assistant_id === "string") {
|
|
2623
|
-
const assistantId = assistant.assistant_id;
|
|
2624
|
-
const matchedAssistant = assistants.find(({ id }) => id === assistantId);
|
|
2625
|
-
if (matchedAssistant && hasName(matchedAssistant)) {
|
|
2626
|
-
const { assistant_id: _assistantId, ...rest } = assistant;
|
|
2627
|
-
return { ...rest, assistant_name: matchedAssistant.name };
|
|
2628
|
-
}
|
|
2629
|
-
}
|
|
2630
|
-
return assistant;
|
|
2631
|
-
});
|
|
2632
|
-
if (transformedAssistants) {
|
|
2633
|
-
const transformedYaml = { ...workflowYaml, assistants: transformedAssistants };
|
|
2634
|
-
const fileName = `${sanitizeFileName(workflow.name)}.yaml`;
|
|
2635
|
-
const filePath = path.join(backupDir, "workflows", fileName);
|
|
2636
|
-
ensureDirectoryExists(filePath);
|
|
2637
|
-
finalYamlContent = yaml.stringify(transformedYaml);
|
|
2638
|
-
fs.writeFileSync(filePath, finalYamlContent, "utf8");
|
|
2639
|
-
}
|
|
2640
|
-
} catch (error) {
|
|
2641
|
-
logger.warn(
|
|
2642
|
-
` \u26A0\uFE0F Failed to transform workflow YAML for ${workflow.name}: ${error instanceof Error ? error.message : String(error)}`
|
|
2643
|
-
);
|
|
3321
|
+
/**
|
|
3322
|
+
* Clean up transaction file after successful completion
|
|
3323
|
+
*/
|
|
3324
|
+
cleanup() {
|
|
3325
|
+
if (fs.existsSync(this.transactionPath)) {
|
|
3326
|
+
fs.unlinkSync(this.transactionPath);
|
|
2644
3327
|
}
|
|
2645
3328
|
}
|
|
2646
|
-
|
|
2647
|
-
id: workflow.id,
|
|
2648
|
-
lastDeployed: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2649
|
-
workflowYamlChecksum: calculateChecksum(finalYamlContent),
|
|
2650
|
-
configChecksum: calculateWorkflowConfigChecksum(resource)
|
|
2651
|
-
};
|
|
2652
|
-
return resource;
|
|
2653
|
-
}
|
|
3329
|
+
};
|
|
2654
3330
|
|
|
2655
3331
|
// src/lib/backupYamlGenerator.ts
|
|
3332
|
+
init_backupTransformers();
|
|
3333
|
+
init_logger();
|
|
2656
3334
|
function generateCodemieYaml(backup, projectName, backupDir, integrationSpecPaths = /* @__PURE__ */ new Map()) {
|
|
2657
3335
|
const integrationIdToAlias = /* @__PURE__ */ new Map();
|
|
2658
3336
|
const integrationArray = [];
|
|
3337
|
+
const skillIdToName = new Map(backup.resources.skills.map((skill) => [skill.id, skill.name]));
|
|
2659
3338
|
for (const integration of backup.resources.integrations) {
|
|
2660
|
-
const
|
|
3339
|
+
const credentialType = integration.credential_type || "integration";
|
|
3340
|
+
const alias = integration.alias || `${String(credentialType).toLowerCase()}_${integration.id.slice(0, 8)}`;
|
|
2661
3341
|
integrationIdToAlias.set(integration.id, alias);
|
|
2662
3342
|
const specPath = integrationSpecPaths.get(integration.id);
|
|
2663
3343
|
let credentialValues = integration.credential_values;
|
|
@@ -2713,17 +3393,20 @@ function generateCodemieYaml(backup, projectName, backupDir, integrationSpecPath
|
|
|
2713
3393
|
},
|
|
2714
3394
|
resources: {
|
|
2715
3395
|
assistants: backup.resources.assistants.map(
|
|
2716
|
-
(assistant) => prepareAssistantForYaml(assistant, backup.state, integrationIdToAlias, integrationSpecPaths)
|
|
3396
|
+
(assistant) => prepareAssistantForYaml(assistant, backup.state, integrationIdToAlias, integrationSpecPaths, skillIdToName)
|
|
2717
3397
|
),
|
|
2718
3398
|
datasources: backup.resources.datasources.map(
|
|
2719
3399
|
(datasource) => prepareDatasourceForYaml(datasource, backup.state, integrationIdToAlias)
|
|
2720
3400
|
),
|
|
2721
3401
|
workflows: backup.resources.workflows.map(
|
|
2722
3402
|
(workflow) => prepareWorkflowForYaml(workflow, backup.state, backup.resources.assistants, backupDir)
|
|
2723
|
-
)
|
|
3403
|
+
),
|
|
3404
|
+
...backup.resources.skills.length > 0 && {
|
|
3405
|
+
skills: backup.resources.skills.map((skill) => prepareSkillForYaml(skill, backup.state))
|
|
3406
|
+
}
|
|
2724
3407
|
}
|
|
2725
3408
|
};
|
|
2726
|
-
return
|
|
3409
|
+
return yaml6.stringify(config);
|
|
2727
3410
|
}
|
|
2728
3411
|
function saveIntegrationOpenApiSpecs(backupData, backupDir) {
|
|
2729
3412
|
const specsDir = path.join(backupDir, "openapi_specs");
|
|
@@ -2778,6 +3461,11 @@ function saveBackupFiles(backupData, backupDir, projectName) {
|
|
|
2778
3461
|
fs.writeFileSync(statePath, JSON.stringify(backupData.state, null, 2), "utf8");
|
|
2779
3462
|
logger.info(` \u2713 Saved state file: state.json`);
|
|
2780
3463
|
}
|
|
3464
|
+
|
|
3465
|
+
// src/backup.ts
|
|
3466
|
+
init_fileUtils();
|
|
3467
|
+
init_logger();
|
|
3468
|
+
init_logger();
|
|
2781
3469
|
function createConcurrentLimiter(maxConcurrent = RATE_LIMITING.MAX_CONCURRENT_REQUESTS) {
|
|
2782
3470
|
return pLimit(maxConcurrent);
|
|
2783
3471
|
}
|
|
@@ -2797,8 +3485,7 @@ async function withRetry(fn, operation, maxAttempts = RATE_LIMITING.RETRY_ATTEMP
|
|
|
2797
3485
|
const delayMs = RATE_LIMITING.RETRY_DELAY_MS * 2 ** (attempt - 1);
|
|
2798
3486
|
logger.warn(` \u26A0\uFE0F Retry ${attempt}/${maxAttempts} for ${operation} after ${delayMs}ms...`);
|
|
2799
3487
|
await new Promise((resolve3) => {
|
|
2800
|
-
|
|
2801
|
-
timerId.unref();
|
|
3488
|
+
setTimeout(resolve3, delayMs);
|
|
2802
3489
|
});
|
|
2803
3490
|
}
|
|
2804
3491
|
}
|
|
@@ -2822,6 +3509,7 @@ async function withTimeout(promise, timeoutMs, operation) {
|
|
|
2822
3509
|
}
|
|
2823
3510
|
|
|
2824
3511
|
// src/backup.ts
|
|
3512
|
+
init_backupTransformers();
|
|
2825
3513
|
async function* streamResources(fetchPage, resourceType) {
|
|
2826
3514
|
let page = 0;
|
|
2827
3515
|
let hasMore = true;
|
|
@@ -2899,21 +3587,42 @@ async function backupAssistants(client, backupData, backupDir, transaction) {
|
|
|
2899
3587
|
}
|
|
2900
3588
|
async function processDatasourceBackup(datasource, client, backupData, transaction) {
|
|
2901
3589
|
logger.info(` \u2022 ${datasource.name} (${datasource.id})`);
|
|
2902
|
-
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
3590
|
+
let full;
|
|
3591
|
+
try {
|
|
3592
|
+
full = await withTimeout(
|
|
3593
|
+
client.datasources.get(datasource.id),
|
|
3594
|
+
TIMEOUTS_MS.DATASOURCE_FETCH,
|
|
3595
|
+
`Timeout fetching datasource ${datasource.id}`
|
|
3596
|
+
);
|
|
3597
|
+
} catch (error) {
|
|
3598
|
+
logger.warn(
|
|
3599
|
+
` \u26A0\uFE0F Failed to fetch full datasource details for ${datasource.name}. Using list response as fallback: ${error instanceof Error ? error.message : String(error)}`
|
|
3600
|
+
);
|
|
3601
|
+
full = datasource;
|
|
3602
|
+
}
|
|
2907
3603
|
backupData.resources.datasources.push(full);
|
|
2908
3604
|
transaction.markCompleted("datasources", datasource.id);
|
|
2909
3605
|
}
|
|
2910
|
-
|
|
3606
|
+
function normalizeProjectName(projectName) {
|
|
3607
|
+
return String(projectName || "").trim().toLowerCase();
|
|
3608
|
+
}
|
|
3609
|
+
function filterResourcesByProject(resources, projectName) {
|
|
3610
|
+
const normalized = normalizeProjectName(projectName);
|
|
3611
|
+
if (!normalized) {
|
|
3612
|
+
return resources;
|
|
3613
|
+
}
|
|
3614
|
+
return resources.filter((r) => normalizeProjectName(r?.project_name) === normalized);
|
|
3615
|
+
}
|
|
3616
|
+
async function backupDatasources(client, backupData, transaction, projectName) {
|
|
2911
3617
|
logger.info("\u{1F4CA} Fetching datasources...");
|
|
2912
|
-
const
|
|
3618
|
+
const allDatasources = [];
|
|
2913
3619
|
for await (const datasource of streamResources((params) => client.datasources.list(params), "datasources")) {
|
|
2914
|
-
|
|
3620
|
+
allDatasources.push(datasource);
|
|
2915
3621
|
}
|
|
2916
|
-
|
|
3622
|
+
const datasources = filterResourcesByProject(allDatasources, projectName);
|
|
3623
|
+
logger.info(
|
|
3624
|
+
` Found ${datasources.length} datasource(s) for project '${projectName}' (from ${allDatasources.length} total)`
|
|
3625
|
+
);
|
|
2917
3626
|
transaction.setTotal("datasources", datasources.length);
|
|
2918
3627
|
const limit = createConcurrentLimiter();
|
|
2919
3628
|
for (const datasource of datasources) {
|
|
@@ -2982,9 +3691,9 @@ async function backupWorkflows(client, backupData, backupDir, transaction) {
|
|
|
2982
3691
|
logger.info(`\u2713 Backed up ${transaction.getData().resources.workflows.completed.length} workflow(s)
|
|
2983
3692
|
`);
|
|
2984
3693
|
}
|
|
2985
|
-
async function backupIntegrations(client, backupData) {
|
|
3694
|
+
async function backupIntegrations(client, backupData, projectName) {
|
|
2986
3695
|
logger.info("\u{1F50C} Fetching integrations...");
|
|
2987
|
-
const
|
|
3696
|
+
const allProjectIntegrations = await withTimeout(
|
|
2988
3697
|
client.integrations.list({
|
|
2989
3698
|
per_page: PAGINATION.DEFAULT_PAGE_SIZE,
|
|
2990
3699
|
page: 0,
|
|
@@ -2993,7 +3702,10 @@ async function backupIntegrations(client, backupData) {
|
|
|
2993
3702
|
TIMEOUTS_MS.INTEGRATION_FETCH,
|
|
2994
3703
|
"Timeout fetching project integrations"
|
|
2995
3704
|
);
|
|
2996
|
-
|
|
3705
|
+
const projectIntegrations = filterResourcesByProject(allProjectIntegrations, projectName);
|
|
3706
|
+
logger.info(
|
|
3707
|
+
` Found ${projectIntegrations.length} project integration(s) for project '${projectName}' (from ${allProjectIntegrations.length} total)`
|
|
3708
|
+
);
|
|
2997
3709
|
for (const integration of projectIntegrations) {
|
|
2998
3710
|
logger.info(` \u2022 ${integration.alias || integration.credential_type} (${integration.id}) [project]`);
|
|
2999
3711
|
backupData.resources.integrations.push(integration);
|
|
@@ -3015,6 +3727,50 @@ async function backupIntegrations(client, backupData) {
|
|
|
3015
3727
|
logger.info(`\u2713 Backed up ${projectIntegrations.length + userIntegrations.length} integration(s)
|
|
3016
3728
|
`);
|
|
3017
3729
|
}
|
|
3730
|
+
async function backupSkills(client, backupData, backupDir, transaction) {
|
|
3731
|
+
logger.info("\u{1F9E0} Fetching skills...");
|
|
3732
|
+
const allSkills = [];
|
|
3733
|
+
for await (const skill of streamResources((params) => client.skills.list(params), "skills")) {
|
|
3734
|
+
allSkills.push(skill);
|
|
3735
|
+
}
|
|
3736
|
+
logger.info(` Found ${allSkills.length} skill(s)`);
|
|
3737
|
+
transaction.setTotal("skills", allSkills.length);
|
|
3738
|
+
const limit = createConcurrentLimiter();
|
|
3739
|
+
for (const skill of allSkills) {
|
|
3740
|
+
if (transaction.isCompleted("skills", skill.id)) {
|
|
3741
|
+
logger.info(` \u21B7 Skipping ${skill.name} (already backed up)`);
|
|
3742
|
+
continue;
|
|
3743
|
+
}
|
|
3744
|
+
try {
|
|
3745
|
+
await limit(
|
|
3746
|
+
() => withRetry(async () => {
|
|
3747
|
+
logger.info(` \u2022 ${skill.name} (${skill.id})`);
|
|
3748
|
+
const full = await withTimeout(
|
|
3749
|
+
client.skills.get(skill.id),
|
|
3750
|
+
TIMEOUTS_MS.SKILL_FETCH,
|
|
3751
|
+
`Timeout fetching skill ${skill.id}`
|
|
3752
|
+
);
|
|
3753
|
+
backupData.resources.skills.push(full);
|
|
3754
|
+
const fileName = `${sanitizeFileName(skill.name)}.skill.md`;
|
|
3755
|
+
const filePath = path.join(backupDir, "skills", fileName);
|
|
3756
|
+
ensureDirectoryExists(filePath);
|
|
3757
|
+
fs.writeFileSync(filePath, full.content || "", "utf8");
|
|
3758
|
+
}, `Backup skill ${skill.name}`)
|
|
3759
|
+
);
|
|
3760
|
+
transaction.markCompleted("skills", skill.id);
|
|
3761
|
+
} catch (error) {
|
|
3762
|
+
const errorDetails = error instanceof Error ? { message: error.message, stack: error.stack, name: error.name } : { message: String(error) };
|
|
3763
|
+
logger.error(` \u274C Failed to backup ${skill.name}:`);
|
|
3764
|
+
logger.error(` ${errorDetails.message}`);
|
|
3765
|
+
if (errorDetails.stack) {
|
|
3766
|
+
logger.error(` Stack trace: ${errorDetails.stack.split("\n").slice(1, 3).join("\n ")}`);
|
|
3767
|
+
}
|
|
3768
|
+
transaction.markFailed("skills", skill.id, errorDetails.message);
|
|
3769
|
+
}
|
|
3770
|
+
}
|
|
3771
|
+
logger.info(`\u2713 Backed up ${transaction.getData().resources.skills.completed.length} skill(s)
|
|
3772
|
+
`);
|
|
3773
|
+
}
|
|
3018
3774
|
function getUniqueBackupDir(baseDir, timestamp) {
|
|
3019
3775
|
let counter = 0;
|
|
3020
3776
|
let finalBackupDir = path.join(baseDir, timestamp);
|
|
@@ -3082,7 +3838,8 @@ async function backupResources(options) {
|
|
|
3082
3838
|
assistants: [],
|
|
3083
3839
|
datasources: [],
|
|
3084
3840
|
workflows: [],
|
|
3085
|
-
integrations: []
|
|
3841
|
+
integrations: [],
|
|
3842
|
+
skills: []
|
|
3086
3843
|
},
|
|
3087
3844
|
state: {
|
|
3088
3845
|
version: "1.0",
|
|
@@ -3091,21 +3848,23 @@ async function backupResources(options) {
|
|
|
3091
3848
|
resources: {
|
|
3092
3849
|
assistants: {},
|
|
3093
3850
|
datasources: {},
|
|
3094
|
-
workflows: {}
|
|
3851
|
+
workflows: {},
|
|
3852
|
+
skills: {}
|
|
3095
3853
|
}
|
|
3096
3854
|
}
|
|
3097
3855
|
};
|
|
3098
|
-
await
|
|
3099
|
-
await
|
|
3856
|
+
await backupSkills(client, backupData, tempBackupDir, transaction);
|
|
3857
|
+
await backupIntegrations(client, backupData, config.project.name);
|
|
3858
|
+
await backupDatasources(client, backupData, transaction, config.project.name);
|
|
3100
3859
|
await backupWorkflows(client, backupData, tempBackupDir, transaction);
|
|
3101
|
-
await
|
|
3860
|
+
await backupAssistants(client, backupData, tempBackupDir, transaction);
|
|
3102
3861
|
const stats = transaction.getData();
|
|
3103
|
-
const totalFailed = stats.resources.assistants.failed.length + stats.resources.datasources.failed.length + stats.resources.workflows.failed.length;
|
|
3862
|
+
const totalFailed = stats.resources.assistants.failed.length + stats.resources.datasources.failed.length + stats.resources.workflows.failed.length + stats.resources.skills.failed.length;
|
|
3104
3863
|
if (totalFailed > 0) {
|
|
3105
3864
|
logger.warn(`
|
|
3106
3865
|
\u26A0\uFE0F Backup completed with ${totalFailed} failed resource(s)`);
|
|
3107
3866
|
logger.warn("Review transaction.json for details\n");
|
|
3108
|
-
const totalResources = stats.resources.assistants.total + stats.resources.datasources.total + stats.resources.workflows.total;
|
|
3867
|
+
const totalResources = stats.resources.assistants.total + stats.resources.datasources.total + stats.resources.workflows.total + stats.resources.skills.total;
|
|
3109
3868
|
const failureRate = totalResources > 0 ? totalFailed / totalResources * 100 : 0;
|
|
3110
3869
|
if (failureRate > 20) {
|
|
3111
3870
|
throw new Error(
|
|
@@ -3127,6 +3886,7 @@ async function backupResources(options) {
|
|
|
3127
3886
|
logger.info(` \u{1F4CA} Datasources: ${backupData.resources.datasources.length}`);
|
|
3128
3887
|
logger.info(` \u{1F504} Workflows: ${backupData.resources.workflows.length}`);
|
|
3129
3888
|
logger.info(` \u{1F50C} Integrations: ${backupData.resources.integrations.length}`);
|
|
3889
|
+
logger.info(` \u{1F9E0} Skills: ${backupData.resources.skills.length}`);
|
|
3130
3890
|
logger.info(`
|
|
3131
3891
|
\u{1F4C1} Location: ${finalBackupDir}
|
|
3132
3892
|
`);
|
|
@@ -3142,6 +3902,356 @@ async function backupResources(options) {
|
|
|
3142
3902
|
throw error;
|
|
3143
3903
|
}
|
|
3144
3904
|
}
|
|
3905
|
+
|
|
3906
|
+
// src/import.ts
|
|
3907
|
+
init_converters();
|
|
3908
|
+
init_fileUtils();
|
|
3909
|
+
init_logger();
|
|
3910
|
+
init_checksumUtils();
|
|
3911
|
+
init_codemieConfigChecksums();
|
|
3912
|
+
var VALID_RESOURCE_TYPES = ["assistant", "datasource", "workflow", "skill"];
|
|
3913
|
+
async function findAssistant(client, slug) {
|
|
3914
|
+
try {
|
|
3915
|
+
const assistant = await withTimeout(
|
|
3916
|
+
client.assistants.getBySlug(slug),
|
|
3917
|
+
TIMEOUTS_MS.ASSISTANT_FETCH,
|
|
3918
|
+
`Timeout fetching assistant by slug "${slug}"`
|
|
3919
|
+
);
|
|
3920
|
+
if (assistant) {
|
|
3921
|
+
return assistant;
|
|
3922
|
+
}
|
|
3923
|
+
} catch {
|
|
3924
|
+
}
|
|
3925
|
+
const match = await findResourceByName2((params) => client.assistants.list(params), slug, "assistant");
|
|
3926
|
+
return withTimeout(
|
|
3927
|
+
client.assistants.get(match.id),
|
|
3928
|
+
TIMEOUTS_MS.ASSISTANT_FETCH,
|
|
3929
|
+
`Timeout fetching assistant "${match.id}"`
|
|
3930
|
+
);
|
|
3931
|
+
}
|
|
3932
|
+
async function findDatasource(client, name) {
|
|
3933
|
+
const match = await findResourceByName2((params) => client.datasources.list(params), name, "datasource");
|
|
3934
|
+
return withTimeout(
|
|
3935
|
+
client.datasources.get(match.id),
|
|
3936
|
+
TIMEOUTS_MS.DATASOURCE_FETCH,
|
|
3937
|
+
`Timeout fetching datasource "${match.id}"`
|
|
3938
|
+
);
|
|
3939
|
+
}
|
|
3940
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
3941
|
+
async function findWorkflow(client, name) {
|
|
3942
|
+
if (UUID_RE.test(name)) {
|
|
3943
|
+
return withTimeout(client.workflows.get(name), TIMEOUTS_MS.WORKFLOW_FETCH, `Timeout fetching workflow "${name}"`);
|
|
3944
|
+
}
|
|
3945
|
+
let match = null;
|
|
3946
|
+
try {
|
|
3947
|
+
match = await findResourceByName2((params) => client.workflows.list(params), name, "workflow");
|
|
3948
|
+
} catch {
|
|
3949
|
+
}
|
|
3950
|
+
if (match) {
|
|
3951
|
+
return withTimeout(
|
|
3952
|
+
client.workflows.get(match.id),
|
|
3953
|
+
TIMEOUTS_MS.WORKFLOW_FETCH,
|
|
3954
|
+
`Timeout fetching workflow "${match.id}"`
|
|
3955
|
+
);
|
|
3956
|
+
}
|
|
3957
|
+
throw new Error(
|
|
3958
|
+
`workflow "${name}" not found on platform via name search. If you know its ID, pass the UUID directly instead of the name.`
|
|
3959
|
+
);
|
|
3960
|
+
}
|
|
3961
|
+
async function findSkill(client, name) {
|
|
3962
|
+
const match = await findResourceByName2((params) => client.skills.list(params), name, "skill");
|
|
3963
|
+
return withTimeout(client.skills.get(match.id), TIMEOUTS_MS.SKILL_FETCH, `Timeout fetching skill "${match.id}"`);
|
|
3964
|
+
}
|
|
3965
|
+
async function findResourceByName2(fetchPage, name, resourceType) {
|
|
3966
|
+
const allResources = [];
|
|
3967
|
+
let page = 0;
|
|
3968
|
+
let hasMore = true;
|
|
3969
|
+
while (hasMore) {
|
|
3970
|
+
const resources = await fetchPage({
|
|
3971
|
+
per_page: PAGINATION.DEFAULT_PAGE_SIZE,
|
|
3972
|
+
page
|
|
3973
|
+
});
|
|
3974
|
+
allResources.push(...resources);
|
|
3975
|
+
if (resources.length < PAGINATION.DEFAULT_PAGE_SIZE) {
|
|
3976
|
+
hasMore = false;
|
|
3977
|
+
} else {
|
|
3978
|
+
page++;
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
const lowerName = name.toLowerCase();
|
|
3982
|
+
const matches = allResources.filter((r) => r.name.toLowerCase() === lowerName);
|
|
3983
|
+
if (matches.length === 0) {
|
|
3984
|
+
throw new Error(
|
|
3985
|
+
`${resourceType} "${name}" not found on platform. Found ${allResources.length} ${resourceType}(s) total.`
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
if (matches.length > 1) {
|
|
3989
|
+
const names = matches.map((m) => ` - ${m.name} (${m.id})`).join("\n");
|
|
3990
|
+
throw new Error(`Multiple ${resourceType}s matching "${name}" found:
|
|
3991
|
+
${names}
|
|
3992
|
+
Please use a more specific name.`);
|
|
3993
|
+
}
|
|
3994
|
+
return matches[0];
|
|
3995
|
+
}
|
|
3996
|
+
function checkResourceExists2(config, resourceType, name) {
|
|
3997
|
+
const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : resourceType === "workflow" ? "workflows" : "skills";
|
|
3998
|
+
const resources = config.resources[pluralType] || [];
|
|
3999
|
+
return resources.some((r) => r.name.toLowerCase() === name.toLowerCase());
|
|
4000
|
+
}
|
|
4001
|
+
function writeResourceFiles(rootDir, resourceType, resource, apiResponse) {
|
|
4002
|
+
const safeName = sanitizeFileName(resource.name);
|
|
4003
|
+
if (resourceType === "workflow") {
|
|
4004
|
+
const workflow = apiResponse;
|
|
4005
|
+
const workflowResource = resource;
|
|
4006
|
+
if (workflow.yaml_config) {
|
|
4007
|
+
const defFileName = `${safeName}.yaml`;
|
|
4008
|
+
const defPath = path.join(rootDir, "workflows", defFileName);
|
|
4009
|
+
ensureDirectoryExists(defPath);
|
|
4010
|
+
fs.writeFileSync(defPath, workflow.yaml_config, "utf8");
|
|
4011
|
+
logger.info(` \u{1F4CB} Saved workflow definition: workflows/${defFileName}`);
|
|
4012
|
+
workflowResource.definition = `workflows/${defFileName}`;
|
|
4013
|
+
}
|
|
4014
|
+
const configPath = path.join(rootDir, "configs", "workflows", `${safeName}.yaml`);
|
|
4015
|
+
const relativeConfigPath = `configs/workflows/${safeName}.yaml`;
|
|
4016
|
+
ensureDirectoryExists(configPath);
|
|
4017
|
+
const yamlContent2 = yaml6.stringify(workflowResource, { lineWidth: 120 });
|
|
4018
|
+
fs.writeFileSync(configPath, yamlContent2, "utf8");
|
|
4019
|
+
logger.info(` \u{1F4C4} Saved workflow config: ${relativeConfigPath}`);
|
|
4020
|
+
return relativeConfigPath;
|
|
4021
|
+
}
|
|
4022
|
+
const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : "skills";
|
|
4023
|
+
const resourceDir = path.join(rootDir, "configs", "imported-resources", pluralType);
|
|
4024
|
+
const resourceFilePath = path.join(resourceDir, `${safeName}.yaml`);
|
|
4025
|
+
const relativeResourcePath = `configs/imported-resources/${pluralType}/${safeName}.yaml`;
|
|
4026
|
+
if (resourceType === "assistant") {
|
|
4027
|
+
const assistant = apiResponse;
|
|
4028
|
+
const assistantResource = resource;
|
|
4029
|
+
if (assistant.system_prompt) {
|
|
4030
|
+
const promptFileName = assistant.slug || safeName;
|
|
4031
|
+
const promptPath = path.join(rootDir, "system_prompts", `${promptFileName}.prompt.md`);
|
|
4032
|
+
ensureDirectoryExists(promptPath);
|
|
4033
|
+
fs.writeFileSync(promptPath, assistant.system_prompt, "utf8");
|
|
4034
|
+
logger.info(` \u{1F4DD} Saved prompt: system_prompts/${promptFileName}.prompt.md`);
|
|
4035
|
+
assistantResource.prompt = `system_prompts/${promptFileName}.prompt.md`;
|
|
4036
|
+
}
|
|
4037
|
+
} else if (resourceType === "skill") {
|
|
4038
|
+
const skill = apiResponse;
|
|
4039
|
+
const skillResource = resource;
|
|
4040
|
+
if (skill.content) {
|
|
4041
|
+
const skillFileName = `${safeName}.skill.md`;
|
|
4042
|
+
const skillPath = path.join(rootDir, "skills", skillFileName);
|
|
4043
|
+
ensureDirectoryExists(skillPath);
|
|
4044
|
+
fs.writeFileSync(skillPath, skill.content, "utf8");
|
|
4045
|
+
logger.info(` \u{1F9E0} Saved skill content: skills/${skillFileName}`);
|
|
4046
|
+
skillResource.file = `skills/${skillFileName}`;
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
ensureDirectoryExists(resourceFilePath);
|
|
4050
|
+
const yamlContent = yaml6.stringify(resource, { lineWidth: 120 });
|
|
4051
|
+
fs.writeFileSync(resourceFilePath, yamlContent, "utf8");
|
|
4052
|
+
logger.info(` \u{1F4C4} Saved resource config: ${relativeResourcePath}`);
|
|
4053
|
+
return relativeResourcePath;
|
|
4054
|
+
}
|
|
4055
|
+
function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relativeResourcePath) {
|
|
4056
|
+
const configPath = path.join(rootDir, codemieConfigPath);
|
|
4057
|
+
if (!fs.existsSync(configPath)) {
|
|
4058
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
4059
|
+
}
|
|
4060
|
+
const content = fs.readFileSync(configPath, "utf8");
|
|
4061
|
+
const doc = yaml6.parseDocument(content);
|
|
4062
|
+
const pluralType = resourceType === "assistant" ? "assistants" : resourceType === "datasource" ? "datasources" : resourceType === "workflow" ? "workflows" : "skills";
|
|
4063
|
+
let resourcesNode = doc.get("resources");
|
|
4064
|
+
if (!resourcesNode) {
|
|
4065
|
+
resourcesNode = doc.createNode({});
|
|
4066
|
+
doc.set("resources", resourcesNode);
|
|
4067
|
+
}
|
|
4068
|
+
let typeArray = doc.getIn(["resources", pluralType]);
|
|
4069
|
+
if (!typeArray) {
|
|
4070
|
+
typeArray = doc.createNode([]);
|
|
4071
|
+
doc.setIn(["resources", pluralType], typeArray);
|
|
4072
|
+
}
|
|
4073
|
+
const importDirective = { $import: relativeResourcePath };
|
|
4074
|
+
const existingImports = typeArray.items.filter((item) => {
|
|
4075
|
+
if (yaml6.isMap(item)) {
|
|
4076
|
+
const importValue = item.get("$import");
|
|
4077
|
+
return importValue === relativeResourcePath;
|
|
4078
|
+
}
|
|
4079
|
+
return false;
|
|
4080
|
+
});
|
|
4081
|
+
if (existingImports.length > 0) {
|
|
4082
|
+
logger.info(` \u21B7 Import directive already exists in ${codemieConfigPath}`);
|
|
4083
|
+
return;
|
|
4084
|
+
}
|
|
4085
|
+
const newNode = doc.createNode(importDirective);
|
|
4086
|
+
typeArray.add(newNode);
|
|
4087
|
+
fs.writeFileSync(configPath, doc.toString(), "utf8");
|
|
4088
|
+
logger.info(` \u2705 Added $import to ${codemieConfigPath} \u2192 resources.${pluralType}`);
|
|
4089
|
+
}
|
|
4090
|
+
function updateState(stateManager, resourceType, resource, apiResponse) {
|
|
4091
|
+
switch (resourceType) {
|
|
4092
|
+
case "assistant": {
|
|
4093
|
+
const assistant = apiResponse;
|
|
4094
|
+
const assistantResource = resource;
|
|
4095
|
+
stateManager.updateAssistantState(resource.name, assistant.id, assistant.system_prompt || "", assistantResource);
|
|
4096
|
+
break;
|
|
4097
|
+
}
|
|
4098
|
+
case "datasource": {
|
|
4099
|
+
const datasource = apiResponse;
|
|
4100
|
+
const datasourceResource = resource;
|
|
4101
|
+
stateManager.updateDatasourceState(resource.name, datasource.id, datasourceResource);
|
|
4102
|
+
break;
|
|
4103
|
+
}
|
|
4104
|
+
case "workflow": {
|
|
4105
|
+
const workflow = apiResponse;
|
|
4106
|
+
const workflowResource = resource;
|
|
4107
|
+
stateManager.updateWorkflowState(
|
|
4108
|
+
resource.name,
|
|
4109
|
+
workflow.id,
|
|
4110
|
+
calculateChecksum(workflow.yaml_config || ""),
|
|
4111
|
+
calculateWorkflowConfigChecksum(workflowResource)
|
|
4112
|
+
);
|
|
4113
|
+
break;
|
|
4114
|
+
}
|
|
4115
|
+
case "skill": {
|
|
4116
|
+
const skill = apiResponse;
|
|
4117
|
+
const skillResource = resource;
|
|
4118
|
+
stateManager.updateSkillState(resource.name, skill.id, skill.content || "", skillResource);
|
|
4119
|
+
break;
|
|
4120
|
+
}
|
|
4121
|
+
}
|
|
4122
|
+
}
|
|
4123
|
+
function buildIntegrationAliasMap(config) {
|
|
4124
|
+
const map = /* @__PURE__ */ new Map();
|
|
4125
|
+
for (const integration of config.imported?.integrations || []) {
|
|
4126
|
+
if (integration.id && integration.alias) {
|
|
4127
|
+
map.set(integration.id, integration.alias);
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
return map;
|
|
4131
|
+
}
|
|
4132
|
+
async function importResource(options) {
|
|
4133
|
+
const { appConfig, resourceType, slug } = options;
|
|
4134
|
+
if (!VALID_RESOURCE_TYPES.includes(resourceType)) {
|
|
4135
|
+
throw new Error(`Invalid resource type: "${resourceType}". Must be one of: ${VALID_RESOURCE_TYPES.join(", ")}`);
|
|
4136
|
+
}
|
|
4137
|
+
const configLoader = new CodemieConfigLoader(appConfig);
|
|
4138
|
+
const config = configLoader.loadConfig();
|
|
4139
|
+
const rootDir = appConfig.rootDir;
|
|
4140
|
+
const stateManager = new StateManager(appConfig);
|
|
4141
|
+
if (checkResourceExists2(config, resourceType, slug)) {
|
|
4142
|
+
throw new Error(
|
|
4143
|
+
`A ${resourceType} named "${slug}" already exists in the configuration. Remove it first or use a different name.`
|
|
4144
|
+
);
|
|
4145
|
+
}
|
|
4146
|
+
logger.info(`
|
|
4147
|
+
\u{1F50D} Importing ${resourceType}: "${slug}"
|
|
4148
|
+
`);
|
|
4149
|
+
logger.info(" Connecting to CodeMie platform...");
|
|
4150
|
+
const client = await createClient(config);
|
|
4151
|
+
const integrationAliasMap = buildIntegrationAliasMap(config);
|
|
4152
|
+
let resource;
|
|
4153
|
+
let apiResponse;
|
|
4154
|
+
switch (resourceType) {
|
|
4155
|
+
case "assistant": {
|
|
4156
|
+
logger.info(` Searching for assistant "${slug}"...`);
|
|
4157
|
+
const assistant = await findAssistant(client, slug);
|
|
4158
|
+
logger.info(` \u2713 Found: ${assistant.name} (${assistant.id})`);
|
|
4159
|
+
apiResponse = assistant;
|
|
4160
|
+
resource = assistantResponseToResource(assistant);
|
|
4161
|
+
if (integrationAliasMap.size > 0) {
|
|
4162
|
+
const { transformToolkits: transformToolkits2, transformMcpServer: transformMcpServer2 } = await Promise.resolve().then(() => (init_backupTransformers(), backupTransformers_exports));
|
|
4163
|
+
const transformedToolkits = transformToolkits2(assistant.toolkits, integrationAliasMap);
|
|
4164
|
+
const transformedMcpServers = assistant.mcp_servers?.map((mcp) => transformMcpServer2(mcp, integrationAliasMap));
|
|
4165
|
+
if (transformedToolkits) {
|
|
4166
|
+
resource.toolkits = transformedToolkits;
|
|
4167
|
+
}
|
|
4168
|
+
if (transformedMcpServers) {
|
|
4169
|
+
resource.mcp_servers = transformedMcpServers;
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
const assistantData = assistant;
|
|
4173
|
+
if (assistantData.skill_ids && assistantData.skill_ids.length > 0) {
|
|
4174
|
+
logger.info(` Resolving ${assistantData.skill_ids.length} skill ID(s)...`);
|
|
4175
|
+
const skillNames = [];
|
|
4176
|
+
for (const skillId of assistantData.skill_ids) {
|
|
4177
|
+
try {
|
|
4178
|
+
const skill = await withTimeout(
|
|
4179
|
+
client.skills.get(skillId),
|
|
4180
|
+
TIMEOUTS_MS.ASSISTANT_FETCH,
|
|
4181
|
+
`Timeout fetching skill "${skillId}"`
|
|
4182
|
+
);
|
|
4183
|
+
skillNames.push(skill.name);
|
|
4184
|
+
logger.info(` \u2713 Resolved skill ${skillId.slice(0, 8)}... \u2192 "${skill.name}"`);
|
|
4185
|
+
} catch {
|
|
4186
|
+
logger.warn(` \u26A0\uFE0F Could not resolve skill ID "${skillId}" \u2014 skipping`);
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
if (skillNames.length > 0) {
|
|
4190
|
+
resource.skills = skillNames;
|
|
4191
|
+
}
|
|
4192
|
+
}
|
|
4193
|
+
break;
|
|
4194
|
+
}
|
|
4195
|
+
case "datasource": {
|
|
4196
|
+
logger.info(` Searching for datasource "${slug}"...`);
|
|
4197
|
+
const datasource = await findDatasource(client, slug);
|
|
4198
|
+
logger.info(` \u2713 Found: ${datasource.name} (${datasource.id})`);
|
|
4199
|
+
apiResponse = datasource;
|
|
4200
|
+
const settingId = datasource.setting_id || "";
|
|
4201
|
+
const integrationAlias = integrationAliasMap.get(settingId);
|
|
4202
|
+
resource = datasourceResponseToResource(datasource, integrationAlias);
|
|
4203
|
+
break;
|
|
4204
|
+
}
|
|
4205
|
+
case "workflow": {
|
|
4206
|
+
logger.info(` Searching for workflow "${slug}"...`);
|
|
4207
|
+
const workflow = await findWorkflow(client, slug);
|
|
4208
|
+
logger.info(` \u2713 Found: ${workflow.name} (${workflow.id})`);
|
|
4209
|
+
if (workflow.name !== slug && checkResourceExists2(config, resourceType, workflow.name)) {
|
|
4210
|
+
throw new Error(
|
|
4211
|
+
`A ${resourceType} named "${workflow.name}" already exists in the configuration. Remove it first or use a different name.`
|
|
4212
|
+
);
|
|
4213
|
+
}
|
|
4214
|
+
apiResponse = workflow;
|
|
4215
|
+
resource = workflowResponseToResource(workflow);
|
|
4216
|
+
break;
|
|
4217
|
+
}
|
|
4218
|
+
case "skill": {
|
|
4219
|
+
logger.info(` Searching for skill "${slug}"...`);
|
|
4220
|
+
const skill = await findSkill(client, slug);
|
|
4221
|
+
logger.info(` \u2713 Found: ${skill.name} (${skill.id})`);
|
|
4222
|
+
apiResponse = skill;
|
|
4223
|
+
resource = skillResponseToResource(skill);
|
|
4224
|
+
break;
|
|
4225
|
+
}
|
|
4226
|
+
}
|
|
4227
|
+
const relativeResourcePath = writeResourceFiles(rootDir, resourceType, resource, apiResponse);
|
|
4228
|
+
addImportToCodemieYaml(rootDir, appConfig.codemieConfig, resourceType, relativeResourcePath);
|
|
4229
|
+
updateState(stateManager, resourceType, resource, apiResponse);
|
|
4230
|
+
logger.info(`
|
|
4231
|
+
\u2705 Successfully imported ${resourceType} "${resource.name}"
|
|
4232
|
+
`);
|
|
4233
|
+
}
|
|
4234
|
+
init_logger();
|
|
4235
|
+
|
|
4236
|
+
// src/lib/terminalPromptHelpers.ts
|
|
4237
|
+
init_commandSpinner();
|
|
4238
|
+
async function promptUser(question, defaultValue) {
|
|
4239
|
+
const rl = readline.createInterface({
|
|
4240
|
+
input: process.stdin,
|
|
4241
|
+
output: process.stdout
|
|
4242
|
+
});
|
|
4243
|
+
const displayQuestion = `${question}: `;
|
|
4244
|
+
const answer = await pauseSpinnerWhile(
|
|
4245
|
+
() => new Promise((resolve3) => {
|
|
4246
|
+
rl.question(displayQuestion, resolve3);
|
|
4247
|
+
})
|
|
4248
|
+
);
|
|
4249
|
+
rl.close();
|
|
4250
|
+
const trimmedAnswer = answer.trim();
|
|
4251
|
+
return trimmedAnswer || defaultValue || "";
|
|
4252
|
+
}
|
|
4253
|
+
|
|
4254
|
+
// src/destroy.ts
|
|
3145
4255
|
async function destroyResources(options) {
|
|
3146
4256
|
logger.info("\u{1F5D1}\uFE0F Destroy all managed resources\n");
|
|
3147
4257
|
logger.info("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
@@ -3153,7 +4263,7 @@ async function destroyResources(options) {
|
|
|
3153
4263
|
logger.info(`Project: ${config.project.name}
|
|
3154
4264
|
`);
|
|
3155
4265
|
const managed = stateManager.getAllManagedResources();
|
|
3156
|
-
const total = managed.assistants.length + managed.datasources.length + managed.workflows.length;
|
|
4266
|
+
const total = managed.assistants.length + managed.datasources.length + managed.workflows.length + managed.skills.length;
|
|
3157
4267
|
if (total === 0) {
|
|
3158
4268
|
logger.info("\u2713 No managed resources found in state\n");
|
|
3159
4269
|
logger.info("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
@@ -3179,21 +4289,19 @@ async function destroyResources(options) {
|
|
|
3179
4289
|
logger.info(` \u2022 ${name}`);
|
|
3180
4290
|
}
|
|
3181
4291
|
}
|
|
4292
|
+
if (managed.skills.length > 0) {
|
|
4293
|
+
logger.info(` \u{1F3AF} Skills: ${managed.skills.length}`);
|
|
4294
|
+
for (const name of managed.skills) {
|
|
4295
|
+
logger.info(` \u2022 ${name}`);
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
3182
4298
|
logger.warn("\n\u26A0\uFE0F WARNING: This will DELETE all resources listed above!");
|
|
3183
4299
|
logger.warn("\u26A0\uFE0F These resources were created through IaC (in state.json)\n");
|
|
3184
4300
|
logger.info("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
3185
4301
|
if (force) {
|
|
3186
4302
|
logger.info("\u{1F680} --force flag detected, skipping confirmation\n");
|
|
3187
4303
|
} else {
|
|
3188
|
-
const
|
|
3189
|
-
const rl = readline.createInterface({
|
|
3190
|
-
input: process.stdin,
|
|
3191
|
-
output: process.stdout
|
|
3192
|
-
});
|
|
3193
|
-
const answer = await new Promise((resolve3) => {
|
|
3194
|
-
rl.question('Type "destroy" to confirm deletion: ', resolve3);
|
|
3195
|
-
});
|
|
3196
|
-
rl.close();
|
|
4304
|
+
const answer = await promptUser('Type "destroy" to confirm deletion');
|
|
3197
4305
|
if (answer.trim().toLowerCase() !== "destroy") {
|
|
3198
4306
|
logger.info("\n\u274C Destruction cancelled\n");
|
|
3199
4307
|
return;
|
|
@@ -3203,7 +4311,7 @@ async function destroyResources(options) {
|
|
|
3203
4311
|
const client = await createClient(config);
|
|
3204
4312
|
const cleanupManager = new CleanupManager(client, stateManager);
|
|
3205
4313
|
const result = await cleanupManager.deleteOrphanedResources(managed);
|
|
3206
|
-
const totalDeleted = result.deleted.assistants.length + result.deleted.datasources.length + result.deleted.workflows.length;
|
|
4314
|
+
const totalDeleted = result.deleted.assistants.length + result.deleted.datasources.length + result.deleted.workflows.length + result.deleted.skills.length;
|
|
3207
4315
|
logger.info("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n");
|
|
3208
4316
|
logger.info("\u{1F4CA} Destruction Summary:\n");
|
|
3209
4317
|
logger.info(` \u2705 Deleted: ${totalDeleted}`);
|
|
@@ -3227,4 +4335,4 @@ async function destroyResources(options) {
|
|
|
3227
4335
|
}
|
|
3228
4336
|
}
|
|
3229
4337
|
|
|
3230
|
-
export { CleanupManager, CodemieConfigLoader, LogLevel, StateManager, backupResources, createClient, deployAssistants, deployDatasources, deployResources, deployWorkflows, destroyResources, logger, previewChanges, validateConfig };
|
|
4338
|
+
export { CleanupManager, CodemieConfigLoader, LogLevel, StateManager, backupResources, createClient, deployAssistants, deployDatasources, deployResources, deploySkills, deployWorkflows, destroyResources, importResource, logger, previewChanges, validateConfig };
|