@codemieai/cdk 0.1.440 → 0.1.446

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/index.js CHANGED
@@ -1,12 +1,878 @@
1
- import * as fs from 'fs';
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/codemieConfigLoader.ts
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 = yaml.parse(content);
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 yaml.parse(content);
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 = yaml.parse(content);
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, path11 = "") {
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, path11);
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, path11);
1095
+ this.resolveObjectReference(current, rootConfig, path12);
208
1096
  return;
209
1097
  }
210
- this.resolveObjectProperties(current, rootConfig, path11);
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, path11) {
1105
+ resolveArrayReferences(arr, rootConfig, path12) {
218
1106
  const result = arr.flatMap((item, i) => {
219
- const contextPath = `${path11}[${i}]`;
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, path11) {
1125
+ resolveObjectReference(current, rootConfig, path12) {
238
1126
  const refPath = current.$ref;
239
- const contextPath = path11 || "root";
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, path11);
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, path11) {
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 = path11 ? `${path11}.${key}` : key;
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, path11 ? `${path11}.${key}` : key);
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
- await client.initialize();
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
- return result;
921
- }
922
- /**
923
- * Get summary of orphaned resources
924
- */
925
- getOrphanedSummary(orphaned) {
926
- const total = orphaned.assistants.length + orphaned.datasources.length + orphaned.workflows.length;
927
- if (total === 0) {
928
- return "No orphaned resources found";
929
- }
930
- const parts = [];
931
- if (orphaned.assistants.length > 0) {
932
- parts.push(`${orphaned.assistants.length} assistant(s)`);
933
- }
934
- if (orphaned.datasources.length > 0) {
935
- parts.push(`${orphaned.datasources.length} datasource(s)`);
936
- }
937
- if (orphaned.workflows.length > 0) {
938
- parts.push(`${orphaned.workflows.length} workflow(s)`);
939
- }
940
- return `Found ${total} orphaned resource(s): ${parts.join(", ")}`;
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
- if (datasource.type === DataSourceType.FILE) {
1149
- return {
1150
- ...base,
1151
- type: DataSourceType.FILE,
1152
- description: datasource.description || ""
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
- logger.warn(` \u26A0\uFE0F Unknown datasource type '${datasource.type}' - saving with basic fields only`);
1156
- return {
1157
- ...base,
1158
- description: datasource.description || ""
1159
- };
1160
- }
1161
- function workflowResponseToResource(workflow) {
1162
- return {
1163
- name: workflow.name,
1164
- description: workflow.description || "",
1165
- definition: `workflows/${workflow.name.toLowerCase().replaceAll(/\s+/g, "-")}.yaml`,
1166
- ...workflow.mode && { mode: workflow.mode },
1167
- ...workflow.shared !== void 0 && { shared: workflow.shared },
1168
- ...workflow.icon_url && { icon_url: workflow.icon_url }
1169
- };
1170
- }
1171
- function isValidCodeParams(params) {
1172
- return typeof params === "object" && params !== null && "link" in params && typeof params.link === "string" && params.link.length > 0;
1173
- }
1174
- function datasourceResourceToCreateParams(datasource, projectName) {
1175
- const {
1176
- $ref: _ref,
1177
- force_reindex: _forceReindex,
1178
- ...sdkFields
1179
- } = datasource;
1180
- const params = {
1181
- ...sdkFields,
1182
- project_name: projectName,
1183
- shared_with_project: datasource.shared_with_project ?? true
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 iacToolToSdk(tool) {
1205
- return {
1206
- ...tool,
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 iacToolkitToSdk(toolkit) {
1212
- return {
1213
- ...toolkit,
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 iacMcpServerToSdk(mcp) {
1221
- return {
1222
- ...mcp,
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
- function assistantResourceToCreateParams(assistant, projectName, promptContent) {
1229
- const {
1230
- prompt: _prompt,
1231
- config: _config,
1232
- model,
1233
- sub_assistants: _subAssistants,
1234
- datasource_names: _datasourceNames,
1235
- toolkits,
1236
- mcp_servers: mcpServers,
1237
- ...sdkFields
1238
- } = assistant;
1239
- return {
1240
- ...sdkFields,
1241
- project: projectName,
1242
- llm_model_type: model,
1243
- system_prompt: promptContent,
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
- const assistantWithResolved = {
1381
- ...assistant,
1382
- assistant_ids: resolvedAssistantIds,
1383
- context: resolvedContext
1384
- };
1385
- const apiParams = assistantResourceToCreateParams(assistantWithResolved, config.project.name, promptContent);
1386
- const existingState = stateManager.getAssistantState(assistant.name);
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 checkAssistantExists(client, assistant.name, stateManager);
2112
+ const existsOnPlatform = await checkSkillExists(client, skill.name, stateManager);
1389
2113
  if (existsOnPlatform) {
1390
- const hasChanged = existingState.promptChecksum !== calculateChecksum(promptContent) || existingState.configChecksum !== configChecksum;
2114
+ const hasChanged = existingState.contentChecksum !== contentChecksum || existingState.configChecksum !== configChecksum;
1391
2115
  if (hasChanged) {
1392
- logger.info(` Updating assistant (ID: ${existingState.id})...`);
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(JSON.stringify(apiParams, null, 2));
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.assistants.update(existingState.id, apiParams);
1399
- logger.info(`\u2713 Updated assistant: ${assistant.name} (${existingState.id})`);
1400
- stateManager.updateAssistantState(assistant.name, existingState.id, promptContent, assistant, buildConfig);
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 Assistant ID from state not found on platform, will create new`);
1408
- logger.info(` Creating new assistant...`);
1409
- if (process.env.DEBUG_API) {
1410
- logger.debug("\n=== DEBUG: API Params ===");
1411
- logger.debug(JSON.stringify(apiParams, null, 2));
1412
- logger.debug("=========================\n");
1413
- }
1414
- const assistantId = await createAssistantAndGetId(client, apiParams, assistant.slug);
1415
- logger.info(`\u2713 Created assistant: ${assistant.name} (${assistantId})`);
1416
- stateManager.updateAssistantState(assistant.name, assistantId, promptContent, assistant, buildConfig);
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 assistant...`);
2157
+ logger.info(` Creating new skill...`);
1421
2158
  if (process.env.DEBUG_API) {
1422
2159
  logger.debug("\n=== DEBUG: API Params ===");
1423
- logger.debug(JSON.stringify(apiParams, null, 2));
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
- const assistantId = await createAssistantAndGetId(client, apiParams, assistant.slug);
1427
- logger.info(`\u2713 Created assistant: ${assistant.name} (${assistantId})`);
1428
- stateManager.updateAssistantState(assistant.name, assistantId, promptContent, assistant, buildConfig);
1429
- stats.created++;
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 ${assistant.name}:`);
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 ("response" in error) {
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:`, JSON.stringify(axiosError.response?.data, null, 2));
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
- await client.datasources.update(updateParams);
1489
- logger.info(
1490
- `\u2713 ${datasource.force_reindex && !hasChanged ? "Reindexed" : "Updated"} datasource: ${datasource.name} (${existingState.id})`
1491
- );
1492
- stateManager.updateDatasourceState(datasource.name, existingState.id, datasource);
1493
- stats.updated++;
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 createDatasourceAndGetId(client, createParams, datasource.name);
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 createDatasourceAndGetId(client, createParams, datasource.name);
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 = yaml.parse(yamlConfigContent);
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 = yaml.stringify(workflowYaml);
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, path11) => {
2623
+ const dfs = (name, path12) => {
1808
2624
  if (recursionStack.has(name)) {
1809
- const cycleStart = path11.indexOf(name);
1810
- const cycle = [...path11.slice(cycleStart), name];
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
- path11.push(name);
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, [...path11])) {
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 = yaml.parse(workflow.definition);
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
- const hasChanged = existingState ? existingState.configChecksum !== configChecksum : false;
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
- if (result.mcp_connect_auth_token && isResolvedIntegration(result.mcp_connect_auth_token)) {
2564
- const authAlias = result.mcp_connect_auth_token.alias || integrationIdToAlias.get(result.mcp_connect_auth_token.id);
2565
- if (authAlias) {
2566
- result.mcp_connect_auth_token = { $ref: `imported.integrations.${authAlias}` };
2567
- } else {
2568
- logger.warn(
2569
- ` \u26A0\uFE0F No alias found for MCP auth token integration ${result.mcp_connect_auth_token.id}, keeping full object`
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
- return result;
2574
- }
2575
- function transformToolkits(toolkits, integrationIdToAlias, integrationSpecPaths) {
2576
- return toolkits?.map(({ toolkit, tools, label, settings_config, is_external, settings }) => ({
2577
- toolkit,
2578
- tools: tools.map((tool) => transformTool(tool, integrationIdToAlias, integrationSpecPaths)),
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
- state.resources.workflows[workflow.name] = {
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 alias = integration.alias || `${integration.credential_type.toLowerCase()}_${integration.id.slice(0, 8)}`;
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 yaml.stringify(config);
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
- const timerId = setTimeout(resolve3, delayMs);
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
- const full = await withTimeout(
2903
- client.datasources.get(datasource.id),
2904
- TIMEOUTS_MS.DATASOURCE_FETCH,
2905
- `Timeout fetching datasource ${datasource.id}`
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
- async function backupDatasources(client, backupData, transaction) {
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 datasources = [];
3618
+ const allDatasources = [];
2913
3619
  for await (const datasource of streamResources((params) => client.datasources.list(params), "datasources")) {
2914
- datasources.push(datasource);
3620
+ allDatasources.push(datasource);
2915
3621
  }
2916
- logger.info(` Found ${datasources.length} datasource(s)`);
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 projectIntegrations = await withTimeout(
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
- logger.info(` Found ${projectIntegrations.length} project integration(s)`);
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 backupAssistants(client, backupData, tempBackupDir, transaction);
3099
- await backupDatasources(client, backupData, transaction);
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 backupIntegrations(client, backupData);
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 readline = await import('readline');
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 };