@codemieai/cdk 0.1.632 → 0.1.633
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/README.md +18 -0
- package/dist/cli/index.js +657 -306
- package/dist/index.d.ts +5 -22
- package/dist/index.js +551 -234
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import ora from 'ora';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
4
|
import { DataSourceType, CodeMieClient, SkillVisibility } from 'codemie-sdk';
|
|
5
|
-
import * as
|
|
6
|
-
import * as
|
|
7
|
-
import
|
|
8
|
-
import * as
|
|
5
|
+
import * as fs11 from 'fs';
|
|
6
|
+
import * as path16 from 'path';
|
|
7
|
+
import path16__default from 'path';
|
|
8
|
+
import * as yaml6 from 'yaml';
|
|
9
9
|
import { program } from '@commander-js/extra-typings';
|
|
10
10
|
import { z } from 'zod';
|
|
11
11
|
import 'dotenv/config';
|
|
@@ -751,15 +751,15 @@ function sanitizeFileName(name, maxLength = 255) {
|
|
|
751
751
|
}
|
|
752
752
|
function validateBackupDirectory(backupDir, minSpaceGB = 1) {
|
|
753
753
|
try {
|
|
754
|
-
const parentDir =
|
|
755
|
-
if (!
|
|
756
|
-
|
|
754
|
+
const parentDir = path16.dirname(backupDir);
|
|
755
|
+
if (!fs11.existsSync(parentDir)) {
|
|
756
|
+
fs11.mkdirSync(parentDir, { recursive: true });
|
|
757
757
|
}
|
|
758
|
-
const testFile =
|
|
759
|
-
|
|
760
|
-
|
|
758
|
+
const testFile = path16.join(parentDir, ".write-test");
|
|
759
|
+
fs11.writeFileSync(testFile, "test");
|
|
760
|
+
fs11.unlinkSync(testFile);
|
|
761
761
|
try {
|
|
762
|
-
const stats =
|
|
762
|
+
const stats = fs11.statfsSync(parentDir);
|
|
763
763
|
const availableGB = stats.bavail * stats.bsize / BYTES_IN_GB;
|
|
764
764
|
if (availableGB < minSpaceGB) {
|
|
765
765
|
throw new Error(`Insufficient disk space: ${availableGB.toFixed(2)}GB available, need ${minSpaceGB}GB`);
|
|
@@ -775,7 +775,7 @@ function validateBackupDirectory(backupDir, minSpaceGB = 1) {
|
|
|
775
775
|
}
|
|
776
776
|
function moveAtomically(tempPath, finalPath) {
|
|
777
777
|
try {
|
|
778
|
-
|
|
778
|
+
fs11.renameSync(tempPath, finalPath);
|
|
779
779
|
} catch (error) {
|
|
780
780
|
const err = error;
|
|
781
781
|
if (err.code === "EEXIST") {
|
|
@@ -785,14 +785,14 @@ function moveAtomically(tempPath, finalPath) {
|
|
|
785
785
|
}
|
|
786
786
|
}
|
|
787
787
|
function cleanupDirectory(dirPath) {
|
|
788
|
-
if (
|
|
789
|
-
|
|
788
|
+
if (fs11.existsSync(dirPath)) {
|
|
789
|
+
fs11.rmSync(dirPath, { recursive: true, force: true });
|
|
790
790
|
}
|
|
791
791
|
}
|
|
792
792
|
function ensureDirectoryExists(filePath) {
|
|
793
|
-
const dir =
|
|
794
|
-
if (!
|
|
795
|
-
|
|
793
|
+
const dir = path16.dirname(filePath);
|
|
794
|
+
if (!fs11.existsSync(dir)) {
|
|
795
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
796
796
|
}
|
|
797
797
|
}
|
|
798
798
|
var BYTES_IN_GB;
|
|
@@ -931,7 +931,7 @@ function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
|
|
|
931
931
|
let finalYamlContent = yamlConfig || "";
|
|
932
932
|
if (yamlConfig) {
|
|
933
933
|
try {
|
|
934
|
-
const workflowYaml =
|
|
934
|
+
const workflowYaml = yaml6.parse(yamlConfig);
|
|
935
935
|
const transformedAssistants = workflowYaml.assistants?.map((assistant) => {
|
|
936
936
|
if (assistant.assistant_id && typeof assistant.assistant_id === "string") {
|
|
937
937
|
const assistantId = assistant.assistant_id;
|
|
@@ -946,10 +946,10 @@ function prepareWorkflowForYaml(workflow, state, assistants, backupDir) {
|
|
|
946
946
|
if (transformedAssistants) {
|
|
947
947
|
const transformedYaml = { ...workflowYaml, assistants: transformedAssistants };
|
|
948
948
|
const fileName = `${sanitizeFileName(workflow.name)}.yaml`;
|
|
949
|
-
const filePath =
|
|
949
|
+
const filePath = path16.join(backupDir, "workflows", fileName);
|
|
950
950
|
ensureDirectoryExists(filePath);
|
|
951
|
-
finalYamlContent =
|
|
952
|
-
|
|
951
|
+
finalYamlContent = yaml6.stringify(transformedYaml);
|
|
952
|
+
fs11.writeFileSync(filePath, finalYamlContent, "utf8");
|
|
953
953
|
}
|
|
954
954
|
} catch (error) {
|
|
955
955
|
logger.warn(
|
|
@@ -1023,7 +1023,7 @@ var init_backupTransformers = __esm({
|
|
|
1023
1023
|
|
|
1024
1024
|
// package.json
|
|
1025
1025
|
var package_default = {
|
|
1026
|
-
version: "0.1.
|
|
1026
|
+
version: "0.1.633"};
|
|
1027
1027
|
var appConfigSchema = z.object({
|
|
1028
1028
|
rootDir: z.string(),
|
|
1029
1029
|
codemieConfig: z.string(),
|
|
@@ -1041,23 +1041,49 @@ var defaultConfig = {
|
|
|
1041
1041
|
};
|
|
1042
1042
|
|
|
1043
1043
|
// src/appConfig/configLoader.ts
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1044
|
+
init_logger();
|
|
1045
|
+
var PROJECT_MARKER_FILE = "codemie.yaml";
|
|
1046
|
+
function loadAppConfig(userConfigPath, rootDir) {
|
|
1047
|
+
if (userConfigPath) {
|
|
1048
|
+
const userConfig = loadUserConfig(userConfigPath);
|
|
1049
|
+
const sanitizedUserConfig = removeEmptyFields(userConfig);
|
|
1050
|
+
const merged = { ...defaultConfig, ...sanitizedUserConfig };
|
|
1051
|
+
if (sanitizedUserConfig.rootDir === void 0 && rootDir) {
|
|
1052
|
+
merged.rootDir = path16.resolve(rootDir);
|
|
1053
|
+
}
|
|
1054
|
+
return appConfigSchema.parse(merged);
|
|
1055
|
+
}
|
|
1056
|
+
if (rootDir) {
|
|
1057
|
+
return { ...defaultConfig, rootDir: path16.resolve(rootDir) };
|
|
1058
|
+
}
|
|
1059
|
+
const detectedRoot = findProjectRoot(process.cwd());
|
|
1060
|
+
if (detectedRoot) {
|
|
1061
|
+
logger.info(`Using detected project root: ${detectedRoot}`);
|
|
1062
|
+
return { ...defaultConfig, rootDir: detectedRoot };
|
|
1063
|
+
}
|
|
1064
|
+
return defaultConfig;
|
|
1065
|
+
}
|
|
1066
|
+
function findProjectRoot(startDir) {
|
|
1067
|
+
let dir = path16.resolve(startDir);
|
|
1068
|
+
while (true) {
|
|
1069
|
+
if (fs11.existsSync(path16.join(dir, PROJECT_MARKER_FILE))) {
|
|
1070
|
+
return dir;
|
|
1071
|
+
}
|
|
1072
|
+
const parentDir = path16.dirname(dir);
|
|
1073
|
+
if (parentDir === dir || dir === path16.parse(dir).root) {
|
|
1074
|
+
return null;
|
|
1075
|
+
}
|
|
1076
|
+
dir = parentDir;
|
|
1047
1077
|
}
|
|
1048
|
-
const userConfig = loadUserConfig(userConfigPath);
|
|
1049
|
-
const sanitizedUserConfig = removeEmptyFields(userConfig);
|
|
1050
|
-
const merged = { ...defaultConfig, ...sanitizedUserConfig };
|
|
1051
|
-
return appConfigSchema.parse(merged);
|
|
1052
1078
|
}
|
|
1053
1079
|
function loadUserConfig(configPath) {
|
|
1054
|
-
const resolvedPath =
|
|
1055
|
-
if (!
|
|
1080
|
+
const resolvedPath = path16.resolve(process.cwd(), configPath);
|
|
1081
|
+
if (!fs11.existsSync(resolvedPath)) {
|
|
1056
1082
|
throw new Error(`Config file not found: ${resolvedPath}`);
|
|
1057
1083
|
}
|
|
1058
1084
|
let raw;
|
|
1059
1085
|
try {
|
|
1060
|
-
raw =
|
|
1086
|
+
raw = fs11.readFileSync(resolvedPath, "utf8");
|
|
1061
1087
|
} catch (error) {
|
|
1062
1088
|
throw new Error(`Failed to read config file: ${resolvedPath}`, { cause: error });
|
|
1063
1089
|
}
|
|
@@ -1119,9 +1145,9 @@ var BackupTransaction = class {
|
|
|
1119
1145
|
isDirty = false;
|
|
1120
1146
|
saveTimer;
|
|
1121
1147
|
constructor(backupDir, transactionId) {
|
|
1122
|
-
this.transactionPath =
|
|
1148
|
+
this.transactionPath = path16.join(backupDir, "transaction.json");
|
|
1123
1149
|
try {
|
|
1124
|
-
const content =
|
|
1150
|
+
const content = fs11.readFileSync(this.transactionPath, "utf8");
|
|
1125
1151
|
this.data = JSON.parse(content);
|
|
1126
1152
|
logger.info(`\u{1F4C2} Resuming backup transaction ${this.data.id}`);
|
|
1127
1153
|
} catch (error) {
|
|
@@ -1153,9 +1179,9 @@ var BackupTransaction = class {
|
|
|
1153
1179
|
}
|
|
1154
1180
|
writeTransactionExclusively(data) {
|
|
1155
1181
|
try {
|
|
1156
|
-
const fd =
|
|
1157
|
-
|
|
1158
|
-
|
|
1182
|
+
const fd = fs11.openSync(this.transactionPath, "wx");
|
|
1183
|
+
fs11.writeSync(fd, JSON.stringify(data, null, 2));
|
|
1184
|
+
fs11.closeSync(fd);
|
|
1159
1185
|
} catch (writeError) {
|
|
1160
1186
|
const writeErr = writeError;
|
|
1161
1187
|
if (writeErr.code === "EEXIST") {
|
|
@@ -1174,7 +1200,7 @@ var BackupTransaction = class {
|
|
|
1174
1200
|
if (delayMs > 0) {
|
|
1175
1201
|
busyWaitDelay(delayMs);
|
|
1176
1202
|
}
|
|
1177
|
-
const content =
|
|
1203
|
+
const content = fs11.readFileSync(this.transactionPath, "utf8");
|
|
1178
1204
|
this.data = JSON.parse(content);
|
|
1179
1205
|
logger.info(`\u{1F4C2} Resuming backup transaction ${this.data.id} (created by concurrent process)`);
|
|
1180
1206
|
break;
|
|
@@ -1197,7 +1223,7 @@ var BackupTransaction = class {
|
|
|
1197
1223
|
*/
|
|
1198
1224
|
async save() {
|
|
1199
1225
|
ensureDirectoryExists(this.transactionPath);
|
|
1200
|
-
const savePromise =
|
|
1226
|
+
const savePromise = fs11.promises.writeFile(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
|
|
1201
1227
|
let timeoutId;
|
|
1202
1228
|
const timeoutPromise = new Promise((_, reject) => {
|
|
1203
1229
|
timeoutId = setTimeout(
|
|
@@ -1223,7 +1249,7 @@ var BackupTransaction = class {
|
|
|
1223
1249
|
*/
|
|
1224
1250
|
saveSyncInternal() {
|
|
1225
1251
|
ensureDirectoryExists(this.transactionPath);
|
|
1226
|
-
|
|
1252
|
+
fs11.writeFileSync(this.transactionPath, JSON.stringify(this.data, null, 2), "utf8");
|
|
1227
1253
|
this.isDirty = false;
|
|
1228
1254
|
}
|
|
1229
1255
|
/**
|
|
@@ -1334,8 +1360,8 @@ var BackupTransaction = class {
|
|
|
1334
1360
|
* Clean up transaction file after successful completion
|
|
1335
1361
|
*/
|
|
1336
1362
|
cleanup() {
|
|
1337
|
-
if (
|
|
1338
|
-
|
|
1363
|
+
if (fs11.existsSync(this.transactionPath)) {
|
|
1364
|
+
fs11.unlinkSync(this.transactionPath);
|
|
1339
1365
|
}
|
|
1340
1366
|
}
|
|
1341
1367
|
};
|
|
@@ -1425,10 +1451,10 @@ function generateCodemieYaml(backup, projectName, backupDir, integrationSpecPath
|
|
|
1425
1451
|
}
|
|
1426
1452
|
}
|
|
1427
1453
|
};
|
|
1428
|
-
return
|
|
1454
|
+
return yaml6.stringify(config);
|
|
1429
1455
|
}
|
|
1430
1456
|
function saveIntegrationOpenApiSpecs(backupData, backupDir) {
|
|
1431
|
-
const specsDir =
|
|
1457
|
+
const specsDir = path16.join(backupDir, "openapi_specs");
|
|
1432
1458
|
let specsCount = 0;
|
|
1433
1459
|
const integrationSpecPaths = /* @__PURE__ */ new Map();
|
|
1434
1460
|
for (const integration of backupData.resources.integrations) {
|
|
@@ -1441,8 +1467,8 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
|
|
|
1441
1467
|
if (specEntry && typeof specEntry.value === "string" && specEntry.value) {
|
|
1442
1468
|
const alias = integration.alias || integration.id;
|
|
1443
1469
|
const fileName = String(alias).toLowerCase().replaceAll(/[^a-z0-9]+/g, "-");
|
|
1444
|
-
if (!
|
|
1445
|
-
|
|
1470
|
+
if (!fs11.existsSync(specsDir)) {
|
|
1471
|
+
fs11.mkdirSync(specsDir, { recursive: true });
|
|
1446
1472
|
}
|
|
1447
1473
|
const specContent = specEntry.value;
|
|
1448
1474
|
let fileExtension = ".yaml";
|
|
@@ -1455,8 +1481,8 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
|
|
|
1455
1481
|
fileExtension = ".yaml";
|
|
1456
1482
|
}
|
|
1457
1483
|
const specFileName = `${fileName}${fileExtension}`;
|
|
1458
|
-
const specPath =
|
|
1459
|
-
|
|
1484
|
+
const specPath = path16.join(specsDir, specFileName);
|
|
1485
|
+
fs11.writeFileSync(specPath, contentToSave, "utf8");
|
|
1460
1486
|
integrationSpecPaths.set(integration.id, `openapi_specs/${specFileName}`);
|
|
1461
1487
|
specsCount++;
|
|
1462
1488
|
}
|
|
@@ -1469,15 +1495,15 @@ function saveIntegrationOpenApiSpecs(backupData, backupDir) {
|
|
|
1469
1495
|
function saveBackupFiles(backupData, backupDir, projectName) {
|
|
1470
1496
|
logger.info("\u{1F4BE} Writing backup files...\n");
|
|
1471
1497
|
const integrationSpecPaths = saveIntegrationOpenApiSpecs(backupData, backupDir);
|
|
1472
|
-
const codemieYamlPath =
|
|
1498
|
+
const codemieYamlPath = path16.join(backupDir, "codemie.yaml");
|
|
1473
1499
|
const codemieYaml = generateCodemieYaml(backupData, projectName, backupDir, integrationSpecPaths);
|
|
1474
|
-
|
|
1500
|
+
fs11.writeFileSync(codemieYamlPath, codemieYaml, "utf8");
|
|
1475
1501
|
logger.info(` \u2713 Saved config file: codemie.yaml`);
|
|
1476
|
-
const backupJsonPath =
|
|
1477
|
-
|
|
1502
|
+
const backupJsonPath = path16.join(backupDir, "backup.json");
|
|
1503
|
+
fs11.writeFileSync(backupJsonPath, JSON.stringify(backupData, null, 2), "utf8");
|
|
1478
1504
|
logger.info(` \u2713 Saved full backup: backup.json`);
|
|
1479
|
-
const statePath =
|
|
1480
|
-
|
|
1505
|
+
const statePath = path16.join(backupDir, "state.json");
|
|
1506
|
+
fs11.writeFileSync(statePath, JSON.stringify(backupData.state, null, 2), "utf8");
|
|
1481
1507
|
logger.info(` \u2713 Saved state file: state.json`);
|
|
1482
1508
|
}
|
|
1483
1509
|
async function createClient(config) {
|
|
@@ -1507,6 +1533,141 @@ function namesEqual(a, b) {
|
|
|
1507
1533
|
return a.toLowerCase() === b.toLowerCase();
|
|
1508
1534
|
}
|
|
1509
1535
|
|
|
1536
|
+
// src/lib/configImports.ts
|
|
1537
|
+
function isPlainObject3(value) {
|
|
1538
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1539
|
+
}
|
|
1540
|
+
function resolveImports(obj, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
|
|
1541
|
+
if (!isPlainObject3(obj)) {
|
|
1542
|
+
return;
|
|
1543
|
+
}
|
|
1544
|
+
for (const key in obj) {
|
|
1545
|
+
const value = obj[key];
|
|
1546
|
+
if (isPlainObject3(value) && "$import" in value) {
|
|
1547
|
+
const importDirective = value;
|
|
1548
|
+
const importPath = path16.join(baseDir, importDirective.$import);
|
|
1549
|
+
const normalizedPath = path16.resolve(importPath);
|
|
1550
|
+
if (visitedFiles.has(normalizedPath)) {
|
|
1551
|
+
throw new Error(
|
|
1552
|
+
`Circular import detected: ${normalizedPath}
|
|
1553
|
+
Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
visitedFiles.add(normalizedPath);
|
|
1557
|
+
const imported = loadYamlFile(importPath);
|
|
1558
|
+
obj[key] = imported;
|
|
1559
|
+
resolveImports(obj[key], path16.dirname(importPath), visitedFiles);
|
|
1560
|
+
visitedFiles.delete(normalizedPath);
|
|
1561
|
+
} else if (Array.isArray(value)) {
|
|
1562
|
+
obj[key] = resolveArrayImports(value, baseDir, visitedFiles);
|
|
1563
|
+
} else if (isPlainObject3(value)) {
|
|
1564
|
+
resolveImports(value, baseDir, visitedFiles);
|
|
1565
|
+
}
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
function resolveArrayImports(arr, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
|
|
1569
|
+
const result = [];
|
|
1570
|
+
for (const item of arr) {
|
|
1571
|
+
if (isPlainObject3(item) && "$import" in item) {
|
|
1572
|
+
const importDirective = item;
|
|
1573
|
+
const importPath = path16.join(baseDir, importDirective.$import);
|
|
1574
|
+
const normalizedPath = path16.resolve(importPath);
|
|
1575
|
+
if (visitedFiles.has(normalizedPath)) {
|
|
1576
|
+
throw new Error(
|
|
1577
|
+
`Circular import detected: ${normalizedPath}
|
|
1578
|
+
Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
1579
|
+
);
|
|
1580
|
+
}
|
|
1581
|
+
visitedFiles.add(normalizedPath);
|
|
1582
|
+
const imported = loadYamlFile(importPath);
|
|
1583
|
+
if (Array.isArray(imported)) {
|
|
1584
|
+
const resolvedArray = resolveArrayImports(imported, path16.dirname(importPath), visitedFiles);
|
|
1585
|
+
for (const resolvedItem of resolvedArray) {
|
|
1586
|
+
result.push(resolvedItem);
|
|
1587
|
+
}
|
|
1588
|
+
} else if (isPlainObject3(imported)) {
|
|
1589
|
+
result.push(imported);
|
|
1590
|
+
resolveImports(imported, path16.dirname(importPath), visitedFiles);
|
|
1591
|
+
} else {
|
|
1592
|
+
throw new TypeError(
|
|
1593
|
+
`Import file ${importPath} must contain either an object or an array. Got: ${typeof imported}`
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
visitedFiles.delete(normalizedPath);
|
|
1597
|
+
} else {
|
|
1598
|
+
result.push(item);
|
|
1599
|
+
if (isPlainObject3(item)) {
|
|
1600
|
+
resolveImports(item, baseDir, visitedFiles);
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
return result;
|
|
1605
|
+
}
|
|
1606
|
+
function loadYamlFile(filePath) {
|
|
1607
|
+
if (!fs11.existsSync(filePath)) {
|
|
1608
|
+
throw new Error(`Import file not found: ${filePath}`);
|
|
1609
|
+
}
|
|
1610
|
+
try {
|
|
1611
|
+
const content = fs11.readFileSync(filePath, "utf8");
|
|
1612
|
+
const parsed = yaml6.parse(content);
|
|
1613
|
+
if (parsed === null || parsed === void 0) {
|
|
1614
|
+
throw new TypeError(`Import file ${filePath} is empty`);
|
|
1615
|
+
}
|
|
1616
|
+
return parsed;
|
|
1617
|
+
} catch (error) {
|
|
1618
|
+
if (error instanceof Error) {
|
|
1619
|
+
throw new TypeError(`Failed to parse YAML file ${filePath}: ${error.message}`);
|
|
1620
|
+
}
|
|
1621
|
+
throw error;
|
|
1622
|
+
}
|
|
1623
|
+
}
|
|
1624
|
+
function findResourceDefinition(rootDir, codemieConfigPath, plural, name) {
|
|
1625
|
+
const configPath = path16.join(rootDir, codemieConfigPath);
|
|
1626
|
+
const config = loadYamlFile(configPath);
|
|
1627
|
+
if (!isPlainObject3(config) || !isPlainObject3(config.resources)) {
|
|
1628
|
+
return void 0;
|
|
1629
|
+
}
|
|
1630
|
+
const list = config.resources[plural];
|
|
1631
|
+
if (!Array.isArray(list)) {
|
|
1632
|
+
return void 0;
|
|
1633
|
+
}
|
|
1634
|
+
return findResourceInList(list, rootDir, null, name, /* @__PURE__ */ new Set());
|
|
1635
|
+
}
|
|
1636
|
+
function findResourceInList(items, baseDir, currentFile, name, visitedFiles) {
|
|
1637
|
+
for (const item of items) {
|
|
1638
|
+
if (!isPlainObject3(item)) {
|
|
1639
|
+
continue;
|
|
1640
|
+
}
|
|
1641
|
+
if (typeof item.$import === "string") {
|
|
1642
|
+
const importPath = path16.join(baseDir, item.$import);
|
|
1643
|
+
const normalizedPath = path16.resolve(importPath);
|
|
1644
|
+
if (visitedFiles.has(normalizedPath)) {
|
|
1645
|
+
throw new Error(
|
|
1646
|
+
`Circular import detected: ${normalizedPath}
|
|
1647
|
+
Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
1648
|
+
);
|
|
1649
|
+
}
|
|
1650
|
+
visitedFiles.add(normalizedPath);
|
|
1651
|
+
const loaded = loadYamlFile(importPath);
|
|
1652
|
+
let found;
|
|
1653
|
+
if (Array.isArray(loaded)) {
|
|
1654
|
+
found = findResourceInList(loaded, path16.dirname(importPath), normalizedPath, name, visitedFiles);
|
|
1655
|
+
} else if (isPlainObject3(loaded) && typeof loaded.name === "string" && namesEqual(loaded.name, name)) {
|
|
1656
|
+
found = { file: normalizedPath, resource: loaded, inList: false };
|
|
1657
|
+
}
|
|
1658
|
+
visitedFiles.delete(normalizedPath);
|
|
1659
|
+
if (found) {
|
|
1660
|
+
return found;
|
|
1661
|
+
}
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
if (typeof item.name === "string" && namesEqual(item.name, name)) {
|
|
1665
|
+
return { file: currentFile, resource: item, inList: true };
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
return void 0;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1510
1671
|
// src/lib/codemieConfigLoader.ts
|
|
1511
1672
|
var CodemieConfigLoader = class {
|
|
1512
1673
|
appConfig;
|
|
@@ -1518,13 +1679,13 @@ var CodemieConfigLoader = class {
|
|
|
1518
1679
|
*/
|
|
1519
1680
|
loadConfig() {
|
|
1520
1681
|
const { rootDir, codemieConfig } = this.appConfig;
|
|
1521
|
-
const configPath =
|
|
1522
|
-
if (!
|
|
1682
|
+
const configPath = path16.join(rootDir, codemieConfig);
|
|
1683
|
+
if (!fs11.existsSync(configPath)) {
|
|
1523
1684
|
throw new Error(`Configuration file not found: ${configPath}`);
|
|
1524
1685
|
}
|
|
1525
|
-
const content =
|
|
1526
|
-
const config =
|
|
1527
|
-
|
|
1686
|
+
const content = fs11.readFileSync(configPath, "utf8");
|
|
1687
|
+
const config = yaml6.parse(content);
|
|
1688
|
+
resolveImports(config, rootDir);
|
|
1528
1689
|
this.substituteEnvVars(config);
|
|
1529
1690
|
this.applyDatasourceDefaults(config);
|
|
1530
1691
|
this.resolveReferencesRecursive(config, config);
|
|
@@ -1536,34 +1697,34 @@ var CodemieConfigLoader = class {
|
|
|
1536
1697
|
*/
|
|
1537
1698
|
loadPrompt(promptPath) {
|
|
1538
1699
|
const { rootDir } = this.appConfig;
|
|
1539
|
-
const fullPath =
|
|
1540
|
-
if (!
|
|
1700
|
+
const fullPath = path16.join(rootDir, promptPath);
|
|
1701
|
+
if (!fs11.existsSync(fullPath)) {
|
|
1541
1702
|
throw new Error(`Prompt file not found: ${fullPath}`);
|
|
1542
1703
|
}
|
|
1543
|
-
return
|
|
1704
|
+
return fs11.readFileSync(fullPath, "utf8");
|
|
1544
1705
|
}
|
|
1545
1706
|
/**
|
|
1546
1707
|
* Load skill content file (.skill.md)
|
|
1547
1708
|
*/
|
|
1548
1709
|
loadSkillContent(contentPath) {
|
|
1549
1710
|
const { rootDir } = this.appConfig;
|
|
1550
|
-
const fullPath =
|
|
1551
|
-
if (!
|
|
1711
|
+
const fullPath = path16.join(rootDir, contentPath);
|
|
1712
|
+
if (!fs11.existsSync(fullPath)) {
|
|
1552
1713
|
throw new Error(`Skill content file not found: ${fullPath}`);
|
|
1553
1714
|
}
|
|
1554
|
-
return
|
|
1715
|
+
return fs11.readFileSync(fullPath, "utf8");
|
|
1555
1716
|
}
|
|
1556
1717
|
/**
|
|
1557
1718
|
* Load assistant configuration file
|
|
1558
1719
|
*/
|
|
1559
1720
|
loadAssistantConfig(configPath) {
|
|
1560
1721
|
const { rootDir } = this.appConfig;
|
|
1561
|
-
const fullPath =
|
|
1562
|
-
if (!
|
|
1722
|
+
const fullPath = path16.join(rootDir, configPath);
|
|
1723
|
+
if (!fs11.existsSync(fullPath)) {
|
|
1563
1724
|
throw new Error(`Config file not found: ${fullPath}`);
|
|
1564
1725
|
}
|
|
1565
|
-
const content =
|
|
1566
|
-
return
|
|
1726
|
+
const content = fs11.readFileSync(fullPath, "utf8");
|
|
1727
|
+
return yaml6.parse(content);
|
|
1567
1728
|
}
|
|
1568
1729
|
/**
|
|
1569
1730
|
* Validate that all referenced files exist
|
|
@@ -1573,13 +1734,14 @@ var CodemieConfigLoader = class {
|
|
|
1573
1734
|
const { rootDir } = this.appConfig;
|
|
1574
1735
|
if (config.resources.assistants) {
|
|
1575
1736
|
for (const assistant of config.resources.assistants) {
|
|
1576
|
-
|
|
1577
|
-
|
|
1737
|
+
if (!assistant.prompt) {
|
|
1738
|
+
errors.push(`Missing 'prompt' for assistant: ${assistant.name}`);
|
|
1739
|
+
} else if (!fs11.existsSync(path16.join(rootDir, assistant.prompt))) {
|
|
1578
1740
|
errors.push(`Prompt file not found for ${assistant.name}: ${assistant.prompt}`);
|
|
1579
1741
|
}
|
|
1580
1742
|
if (assistant.config) {
|
|
1581
|
-
const configPath =
|
|
1582
|
-
if (!
|
|
1743
|
+
const configPath = path16.join(rootDir, assistant.config);
|
|
1744
|
+
if (!fs11.existsSync(configPath)) {
|
|
1583
1745
|
errors.push(`Config file not found for ${assistant.name}: ${assistant.config}`);
|
|
1584
1746
|
}
|
|
1585
1747
|
}
|
|
@@ -1593,8 +1755,9 @@ var CodemieConfigLoader = class {
|
|
|
1593
1755
|
}
|
|
1594
1756
|
if (config.resources.skills) {
|
|
1595
1757
|
for (const skill of config.resources.skills) {
|
|
1596
|
-
|
|
1597
|
-
|
|
1758
|
+
if (!skill.file) {
|
|
1759
|
+
errors.push(`Missing 'file' for skill: ${skill.name}`);
|
|
1760
|
+
} else if (!fs11.existsSync(path16.join(rootDir, skill.file))) {
|
|
1598
1761
|
errors.push(`Content file not found for skill ${skill.name}: ${skill.file}`);
|
|
1599
1762
|
}
|
|
1600
1763
|
if (!skill.description) {
|
|
@@ -1615,109 +1778,6 @@ var CodemieConfigLoader = class {
|
|
|
1615
1778
|
errors
|
|
1616
1779
|
};
|
|
1617
1780
|
}
|
|
1618
|
-
/**
|
|
1619
|
-
* Resolve $import directives recursively
|
|
1620
|
-
* Automatically handles:
|
|
1621
|
-
* 1. Single resource import (object)
|
|
1622
|
-
* 2. Multiple resources import (array)
|
|
1623
|
-
* 3. Mixed inline + imports
|
|
1624
|
-
* Includes circular import detection to prevent infinite recursion
|
|
1625
|
-
*/
|
|
1626
|
-
resolveImports(obj, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
|
|
1627
|
-
if (!this.isPlainObject(obj)) {
|
|
1628
|
-
return;
|
|
1629
|
-
}
|
|
1630
|
-
for (const key in obj) {
|
|
1631
|
-
const value = obj[key];
|
|
1632
|
-
if (this.isPlainObject(value) && "$import" in value) {
|
|
1633
|
-
const importDirective = value;
|
|
1634
|
-
const importPath = path6.join(baseDir, importDirective.$import);
|
|
1635
|
-
const normalizedPath = path6.resolve(importPath);
|
|
1636
|
-
if (visitedFiles.has(normalizedPath)) {
|
|
1637
|
-
throw new Error(
|
|
1638
|
-
`Circular import detected: ${normalizedPath}
|
|
1639
|
-
Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
1640
|
-
);
|
|
1641
|
-
}
|
|
1642
|
-
visitedFiles.add(normalizedPath);
|
|
1643
|
-
const imported = this.loadYamlFile(importPath);
|
|
1644
|
-
obj[key] = imported;
|
|
1645
|
-
this.resolveImports(obj[key], path6.dirname(importPath), visitedFiles);
|
|
1646
|
-
visitedFiles.delete(normalizedPath);
|
|
1647
|
-
} else if (Array.isArray(value)) {
|
|
1648
|
-
obj[key] = this.resolveArrayImports(value, baseDir, visitedFiles);
|
|
1649
|
-
} else if (this.isPlainObject(value)) {
|
|
1650
|
-
this.resolveImports(value, baseDir, visitedFiles);
|
|
1651
|
-
}
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
/**
|
|
1655
|
-
* Resolve imports in arrays
|
|
1656
|
-
* Intelligently handles:
|
|
1657
|
-
* - Inline objects (keep as-is)
|
|
1658
|
-
* - $import with single object (add to array)
|
|
1659
|
-
* - $import with array (flatten into parent array)
|
|
1660
|
-
* Includes circular import detection
|
|
1661
|
-
*/
|
|
1662
|
-
resolveArrayImports(arr, baseDir, visitedFiles = /* @__PURE__ */ new Set()) {
|
|
1663
|
-
const result = [];
|
|
1664
|
-
for (const item of arr) {
|
|
1665
|
-
if (this.isPlainObject(item) && "$import" in item) {
|
|
1666
|
-
const importDirective = item;
|
|
1667
|
-
const importPath = path6.join(baseDir, importDirective.$import);
|
|
1668
|
-
const normalizedPath = path6.resolve(importPath);
|
|
1669
|
-
if (visitedFiles.has(normalizedPath)) {
|
|
1670
|
-
throw new Error(
|
|
1671
|
-
`Circular import detected: ${normalizedPath}
|
|
1672
|
-
Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
1673
|
-
);
|
|
1674
|
-
}
|
|
1675
|
-
visitedFiles.add(normalizedPath);
|
|
1676
|
-
const imported = this.loadYamlFile(importPath);
|
|
1677
|
-
if (Array.isArray(imported)) {
|
|
1678
|
-
const resolvedArray = this.resolveArrayImports(imported, path6.dirname(importPath), visitedFiles);
|
|
1679
|
-
for (const resolvedItem of resolvedArray) {
|
|
1680
|
-
result.push(resolvedItem);
|
|
1681
|
-
}
|
|
1682
|
-
} else if (this.isPlainObject(imported)) {
|
|
1683
|
-
result.push(imported);
|
|
1684
|
-
this.resolveImports(imported, path6.dirname(importPath), visitedFiles);
|
|
1685
|
-
} else {
|
|
1686
|
-
throw new TypeError(
|
|
1687
|
-
`Import file ${importPath} must contain either an object or an array. Got: ${typeof imported}`
|
|
1688
|
-
);
|
|
1689
|
-
}
|
|
1690
|
-
visitedFiles.delete(normalizedPath);
|
|
1691
|
-
} else {
|
|
1692
|
-
result.push(item);
|
|
1693
|
-
if (this.isPlainObject(item)) {
|
|
1694
|
-
this.resolveImports(item, baseDir, visitedFiles);
|
|
1695
|
-
}
|
|
1696
|
-
}
|
|
1697
|
-
}
|
|
1698
|
-
return result;
|
|
1699
|
-
}
|
|
1700
|
-
/**
|
|
1701
|
-
* Load YAML file with error handling
|
|
1702
|
-
*/
|
|
1703
|
-
loadYamlFile(filePath) {
|
|
1704
|
-
if (!fs6.existsSync(filePath)) {
|
|
1705
|
-
throw new Error(`Import file not found: ${filePath}`);
|
|
1706
|
-
}
|
|
1707
|
-
try {
|
|
1708
|
-
const content = fs6.readFileSync(filePath, "utf8");
|
|
1709
|
-
const parsed = yaml5.parse(content);
|
|
1710
|
-
if (parsed === null || parsed === void 0) {
|
|
1711
|
-
throw new TypeError(`Import file ${filePath} is empty`);
|
|
1712
|
-
}
|
|
1713
|
-
return parsed;
|
|
1714
|
-
} catch (error) {
|
|
1715
|
-
if (error instanceof Error) {
|
|
1716
|
-
throw new TypeError(`Failed to parse YAML file ${filePath}: ${error.message}`);
|
|
1717
|
-
}
|
|
1718
|
-
throw error;
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
1781
|
/**
|
|
1722
1782
|
* Recursively resolve all $ref in an object/array structure
|
|
1723
1783
|
* Handles both object references { $ref: "path" } and string references "$ref:path"
|
|
@@ -1726,28 +1786,28 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
1726
1786
|
* @param rootConfig - Root config object (constant reference for resolving paths like "imported.integrations.xxx")
|
|
1727
1787
|
* @param path - Current path in config tree (for error messages, e.g., "resources.assistants[0].toolkits")
|
|
1728
1788
|
*/
|
|
1729
|
-
resolveReferencesRecursive(current, rootConfig,
|
|
1789
|
+
resolveReferencesRecursive(current, rootConfig, path17 = "") {
|
|
1730
1790
|
if (!current || typeof current !== "object") {
|
|
1731
1791
|
return;
|
|
1732
1792
|
}
|
|
1733
1793
|
if (Array.isArray(current)) {
|
|
1734
|
-
this.resolveArrayReferences(current, rootConfig,
|
|
1794
|
+
this.resolveArrayReferences(current, rootConfig, path17);
|
|
1735
1795
|
return;
|
|
1736
1796
|
}
|
|
1737
1797
|
if ("$ref" in current && typeof current.$ref === "string") {
|
|
1738
|
-
this.resolveObjectReference(current, rootConfig,
|
|
1798
|
+
this.resolveObjectReference(current, rootConfig, path17);
|
|
1739
1799
|
return;
|
|
1740
1800
|
}
|
|
1741
|
-
this.resolveObjectProperties(current, rootConfig,
|
|
1801
|
+
this.resolveObjectProperties(current, rootConfig, path17);
|
|
1742
1802
|
}
|
|
1743
1803
|
/**
|
|
1744
1804
|
* Resolve $ref items in arrays and flatten if they point to arrays
|
|
1745
1805
|
* Example: [{ $ref: "context_definitions.repos" }] where repos is [item1, item2]
|
|
1746
1806
|
* becomes [item1, item2]
|
|
1747
1807
|
*/
|
|
1748
|
-
resolveArrayReferences(arr, rootConfig,
|
|
1808
|
+
resolveArrayReferences(arr, rootConfig, path17) {
|
|
1749
1809
|
const result = arr.flatMap((item, i) => {
|
|
1750
|
-
const contextPath = `${
|
|
1810
|
+
const contextPath = `${path17}[${i}]`;
|
|
1751
1811
|
if (!this.isRefObject(item) || item.$ref.startsWith("#")) {
|
|
1752
1812
|
this.resolveReferencesRecursive(item, rootConfig, contextPath);
|
|
1753
1813
|
return [item];
|
|
@@ -1765,9 +1825,9 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
1765
1825
|
* Resolve object reference: { $ref: "path" }
|
|
1766
1826
|
* Replaces object with resolved data (in-place mutation)
|
|
1767
1827
|
*/
|
|
1768
|
-
resolveObjectReference(current, rootConfig,
|
|
1828
|
+
resolveObjectReference(current, rootConfig, path17) {
|
|
1769
1829
|
const refPath = current.$ref;
|
|
1770
|
-
const contextPath =
|
|
1830
|
+
const contextPath = path17 || "root";
|
|
1771
1831
|
if (refPath.startsWith("#")) {
|
|
1772
1832
|
return;
|
|
1773
1833
|
}
|
|
@@ -1777,24 +1837,24 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
|
|
|
1777
1837
|
delete current[key];
|
|
1778
1838
|
}
|
|
1779
1839
|
Object.assign(current, filteredResolved);
|
|
1780
|
-
this.resolveReferencesRecursive(current, rootConfig,
|
|
1840
|
+
this.resolveReferencesRecursive(current, rootConfig, path17);
|
|
1781
1841
|
}
|
|
1782
1842
|
/**
|
|
1783
1843
|
* Resolve object properties recursively
|
|
1784
1844
|
* Handles both nested objects and "$ref:path" string references
|
|
1785
1845
|
*/
|
|
1786
|
-
resolveObjectProperties(current, rootConfig,
|
|
1846
|
+
resolveObjectProperties(current, rootConfig, path17) {
|
|
1787
1847
|
for (const [key, value] of Object.entries(current)) {
|
|
1788
1848
|
if (typeof value === "string" && value.startsWith("$ref:")) {
|
|
1789
1849
|
const refPath = value.slice(5);
|
|
1790
1850
|
if (refPath.startsWith("#")) {
|
|
1791
1851
|
continue;
|
|
1792
1852
|
}
|
|
1793
|
-
const contextPath =
|
|
1853
|
+
const contextPath = path17 ? `${path17}.${key}` : key;
|
|
1794
1854
|
const resolved = this.resolveReference(rootConfig, refPath, contextPath);
|
|
1795
1855
|
current[key] = resolved;
|
|
1796
1856
|
} else {
|
|
1797
|
-
this.resolveReferencesRecursive(value, rootConfig,
|
|
1857
|
+
this.resolveReferencesRecursive(value, rootConfig, path17 ? `${path17}.${key}` : key);
|
|
1798
1858
|
}
|
|
1799
1859
|
}
|
|
1800
1860
|
}
|
|
@@ -2045,9 +2105,9 @@ async function saveAssistantToBackup(assistant, client, backupData, backupDir) {
|
|
|
2045
2105
|
backupData.resources.assistants.push(full);
|
|
2046
2106
|
if (full.system_prompt) {
|
|
2047
2107
|
const fileName = `${full.slug || sanitizeFileName(full.name)}.prompt.md`;
|
|
2048
|
-
const filePath =
|
|
2108
|
+
const filePath = path16.join(backupDir, "system_prompts", fileName);
|
|
2049
2109
|
ensureDirectoryExists(filePath);
|
|
2050
|
-
|
|
2110
|
+
fs11.writeFileSync(filePath, full.system_prompt, "utf8");
|
|
2051
2111
|
}
|
|
2052
2112
|
}
|
|
2053
2113
|
async function backupAssistants(client, backupData, backupDir, transaction) {
|
|
@@ -2173,9 +2233,9 @@ async function backupWorkflows(client, backupData, backupDir, transaction) {
|
|
|
2173
2233
|
backupData.resources.workflows.push(full);
|
|
2174
2234
|
if (full.yaml_config) {
|
|
2175
2235
|
const fileName = `${sanitizeFileName(full.name)}.yaml`;
|
|
2176
|
-
const filePath =
|
|
2236
|
+
const filePath = path16.join(backupDir, "workflows", fileName);
|
|
2177
2237
|
ensureDirectoryExists(filePath);
|
|
2178
|
-
|
|
2238
|
+
fs11.writeFileSync(filePath, full.yaml_config, "utf8");
|
|
2179
2239
|
}
|
|
2180
2240
|
transaction.markCompleted("workflows", workflow.id);
|
|
2181
2241
|
} catch (error) {
|
|
@@ -2278,9 +2338,9 @@ async function backupSkills(client, backupData, backupDir, transaction, projectN
|
|
|
2278
2338
|
backupData.resources.skills.push(full);
|
|
2279
2339
|
if (fullIsProjectOwned) {
|
|
2280
2340
|
const fileName = `${sanitizeFileName(skill.name)}.skill.md`;
|
|
2281
|
-
const filePath =
|
|
2341
|
+
const filePath = path16.join(backupDir, "skills", fileName);
|
|
2282
2342
|
ensureDirectoryExists(filePath);
|
|
2283
|
-
|
|
2343
|
+
fs11.writeFileSync(filePath, full.content || "", "utf8");
|
|
2284
2344
|
} else {
|
|
2285
2345
|
logger.info(` \u21B7 Skipping content file for imported skill ${skill.name} (owner: ${full.project})`);
|
|
2286
2346
|
}
|
|
@@ -2302,17 +2362,17 @@ async function backupSkills(client, backupData, backupDir, transaction, projectN
|
|
|
2302
2362
|
}
|
|
2303
2363
|
function getUniqueBackupDir(baseDir, timestamp) {
|
|
2304
2364
|
let counter = 0;
|
|
2305
|
-
let finalBackupDir =
|
|
2306
|
-
while (
|
|
2365
|
+
let finalBackupDir = path16.join(baseDir, timestamp);
|
|
2366
|
+
while (fs11.existsSync(finalBackupDir)) {
|
|
2307
2367
|
counter++;
|
|
2308
|
-
finalBackupDir =
|
|
2368
|
+
finalBackupDir = path16.join(baseDir, `${timestamp}-${counter}`);
|
|
2309
2369
|
}
|
|
2310
2370
|
const dirSuffix = counter > 0 ? `${timestamp}-${counter}` : timestamp;
|
|
2311
|
-
const tempBackupDir =
|
|
2371
|
+
const tempBackupDir = path16.join(baseDir, `${BACKUP.TEMP_DIR_PREFIX}${dirSuffix}`);
|
|
2312
2372
|
return { finalDir: finalBackupDir, tempDir: tempBackupDir };
|
|
2313
2373
|
}
|
|
2314
2374
|
function performCleanup(tempBackupDir, transaction) {
|
|
2315
|
-
if (
|
|
2375
|
+
if (fs11.existsSync(tempBackupDir)) {
|
|
2316
2376
|
try {
|
|
2317
2377
|
logger.info(`\u{1F9F9} Rolling back: cleaning up ${tempBackupDir}...`);
|
|
2318
2378
|
cleanupDirectory(tempBackupDir);
|
|
@@ -2341,7 +2401,7 @@ async function backupResources(options) {
|
|
|
2341
2401
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "").replaceAll(":", "-");
|
|
2342
2402
|
const { appConfig } = options;
|
|
2343
2403
|
const { rootDir, backupsDirectory } = appConfig;
|
|
2344
|
-
const backupBaseDir =
|
|
2404
|
+
const backupBaseDir = path16.join(rootDir, backupsDirectory);
|
|
2345
2405
|
const { finalDir: finalBackupDir, tempDir: tempBackupDir } = getUniqueBackupDir(backupBaseDir, timestamp);
|
|
2346
2406
|
let transaction = null;
|
|
2347
2407
|
try {
|
|
@@ -2351,8 +2411,8 @@ async function backupResources(options) {
|
|
|
2351
2411
|
logger.info("\u{1F50C} Connecting to Codemie API...");
|
|
2352
2412
|
const client = await createClient(config);
|
|
2353
2413
|
logger.info("\u2713 Connected to Codemie API\n");
|
|
2354
|
-
if (!
|
|
2355
|
-
|
|
2414
|
+
if (!fs11.existsSync(tempBackupDir)) {
|
|
2415
|
+
fs11.mkdirSync(tempBackupDir, { recursive: true });
|
|
2356
2416
|
}
|
|
2357
2417
|
logger.info(`\u{1F4C1} Temporary backup directory: ${tempBackupDir}
|
|
2358
2418
|
`);
|
|
@@ -2933,16 +2993,16 @@ var StateManager = class {
|
|
|
2933
2993
|
statePath;
|
|
2934
2994
|
constructor(appConfig) {
|
|
2935
2995
|
const { rootDir, codemieState } = appConfig;
|
|
2936
|
-
this.statePath =
|
|
2996
|
+
this.statePath = path16.join(rootDir, codemieState);
|
|
2937
2997
|
}
|
|
2938
2998
|
/**
|
|
2939
2999
|
* Load state file
|
|
2940
3000
|
*/
|
|
2941
3001
|
loadState() {
|
|
2942
|
-
if (!
|
|
3002
|
+
if (!fs11.existsSync(this.statePath)) {
|
|
2943
3003
|
return this.createEmptyState();
|
|
2944
3004
|
}
|
|
2945
|
-
const content =
|
|
3005
|
+
const content = fs11.readFileSync(this.statePath, "utf8");
|
|
2946
3006
|
const state = JSON.parse(content);
|
|
2947
3007
|
if (!state.resources) {
|
|
2948
3008
|
state.resources = {
|
|
@@ -2970,12 +3030,12 @@ var StateManager = class {
|
|
|
2970
3030
|
* Save state file
|
|
2971
3031
|
*/
|
|
2972
3032
|
saveState(state) {
|
|
2973
|
-
const dir =
|
|
2974
|
-
if (!
|
|
2975
|
-
|
|
3033
|
+
const dir = path16.dirname(this.statePath);
|
|
3034
|
+
if (!fs11.existsSync(dir)) {
|
|
3035
|
+
fs11.mkdirSync(dir, { recursive: true });
|
|
2976
3036
|
}
|
|
2977
3037
|
state.lastSync = (/* @__PURE__ */ new Date()).toISOString();
|
|
2978
|
-
|
|
3038
|
+
fs11.writeFileSync(this.statePath, JSON.stringify(state, null, 2));
|
|
2979
3039
|
}
|
|
2980
3040
|
/**
|
|
2981
3041
|
* Create empty state structure
|
|
@@ -3674,12 +3734,12 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
|
|
|
3674
3734
|
for (const workflow of config.resources.workflows) {
|
|
3675
3735
|
try {
|
|
3676
3736
|
logger.info(`Processing: ${workflow.name}`);
|
|
3677
|
-
const yamlPath =
|
|
3678
|
-
if (!
|
|
3737
|
+
const yamlPath = path16.join(rootDir, workflow.definition);
|
|
3738
|
+
if (!fs11.existsSync(yamlPath)) {
|
|
3679
3739
|
throw new Error(`Workflow definition file not found: ${workflow.definition}`);
|
|
3680
3740
|
}
|
|
3681
|
-
const yamlConfigContent =
|
|
3682
|
-
const workflowYaml =
|
|
3741
|
+
const yamlConfigContent = fs11.readFileSync(yamlPath, "utf8");
|
|
3742
|
+
const workflowYaml = yaml6.parse(yamlConfigContent);
|
|
3683
3743
|
const referencesToResolve = [];
|
|
3684
3744
|
for (const assistant of workflowYaml.assistants ?? []) {
|
|
3685
3745
|
const isInline = !assistant.assistant_name && assistant.model && assistant.system_prompt;
|
|
@@ -3720,7 +3780,7 @@ async function deployWorkflows(config, client, stateManager, rootDir = process.c
|
|
|
3720
3780
|
});
|
|
3721
3781
|
}
|
|
3722
3782
|
}
|
|
3723
|
-
const yamlConfig =
|
|
3783
|
+
const yamlConfig = yaml6.stringify(workflowYaml);
|
|
3724
3784
|
const workflowYamlChecksum = calculateChecksum(yamlConfigContent);
|
|
3725
3785
|
const configChecksum = calculateWorkflowConfigChecksum(workflow);
|
|
3726
3786
|
const existingState = stateManager.getWorkflowState(workflow.name);
|
|
@@ -4050,14 +4110,34 @@ async function main3(options) {
|
|
|
4050
4110
|
}
|
|
4051
4111
|
|
|
4052
4112
|
// src/import.ts
|
|
4113
|
+
init_checksumUtils();
|
|
4114
|
+
init_codemieConfigChecksums();
|
|
4053
4115
|
init_converters();
|
|
4054
|
-
init_skillUtils();
|
|
4055
4116
|
init_fileUtils();
|
|
4056
4117
|
init_logger();
|
|
4057
|
-
|
|
4058
|
-
|
|
4118
|
+
|
|
4119
|
+
// src/lib/resourceTypes.ts
|
|
4059
4120
|
var VALID_RESOURCE_TYPES = ["assistant", "datasource", "workflow", "skill"];
|
|
4121
|
+
var PLURAL_BY_RESOURCE_TYPE = {
|
|
4122
|
+
assistant: "assistants",
|
|
4123
|
+
datasource: "datasources",
|
|
4124
|
+
workflow: "workflows",
|
|
4125
|
+
skill: "skills"
|
|
4126
|
+
};
|
|
4127
|
+
function pluralTypeOf(resourceType) {
|
|
4128
|
+
return PLURAL_BY_RESOURCE_TYPE[resourceType];
|
|
4129
|
+
}
|
|
4130
|
+
|
|
4131
|
+
// src/import.ts
|
|
4132
|
+
init_skillUtils();
|
|
4060
4133
|
async function findAssistant(client, slug) {
|
|
4134
|
+
if (UUID_RE.test(slug)) {
|
|
4135
|
+
return withTimeout(
|
|
4136
|
+
client.assistants.get(slug),
|
|
4137
|
+
TIMEOUTS_MS.ASSISTANT_FETCH,
|
|
4138
|
+
`Timeout fetching assistant "${slug}"`
|
|
4139
|
+
);
|
|
4140
|
+
}
|
|
4061
4141
|
try {
|
|
4062
4142
|
const assistant = await withTimeout(
|
|
4063
4143
|
client.assistants.getBySlug(slug),
|
|
@@ -4141,7 +4221,7 @@ Please use a more specific name.`);
|
|
|
4141
4221
|
return matches[0];
|
|
4142
4222
|
}
|
|
4143
4223
|
function checkResourceExists2(config, resourceType, name) {
|
|
4144
|
-
const pluralType = resourceType
|
|
4224
|
+
const pluralType = pluralTypeOf(resourceType);
|
|
4145
4225
|
const resources = config.resources[pluralType] || [];
|
|
4146
4226
|
if (resources.some((r) => namesEqual(r.name, name))) {
|
|
4147
4227
|
return true;
|
|
@@ -4152,68 +4232,252 @@ function checkResourceExists2(config, resourceType, name) {
|
|
|
4152
4232
|
}
|
|
4153
4233
|
return false;
|
|
4154
4234
|
}
|
|
4235
|
+
function findManagedNameById(stateManager, resourceType, id) {
|
|
4236
|
+
const state = stateManager.loadState();
|
|
4237
|
+
const pluralType = pluralTypeOf(resourceType);
|
|
4238
|
+
const resources = state.resources[pluralType] || {};
|
|
4239
|
+
const matches = Object.keys(resources).filter((name) => resources[name]?.id === id);
|
|
4240
|
+
if (matches.length > 1) {
|
|
4241
|
+
throw new Error(
|
|
4242
|
+
`state.json is ambiguous: ${matches.length} ${pluralType} entries share platform id ${id} (${matches.map((name) => `"${name}"`).join(", ")}). Remove the duplicate entries so this ${resourceType} can be matched unambiguously.`
|
|
4243
|
+
);
|
|
4244
|
+
}
|
|
4245
|
+
return matches[0];
|
|
4246
|
+
}
|
|
4247
|
+
function nameClashError(resourceType, name) {
|
|
4248
|
+
return new Error(
|
|
4249
|
+
`A ${resourceType} named "${name}" already exists in the configuration. Re-run with --update to replace it with the platform version, or remove it first.`
|
|
4250
|
+
);
|
|
4251
|
+
}
|
|
4252
|
+
function assertResolvedNameIsFree(config, stateManager, resourceType, slug, resolvedName, id, update) {
|
|
4253
|
+
if (!update && !namesEqual(slug, resolvedName) && checkResourceExists2(config, resourceType, resolvedName)) {
|
|
4254
|
+
throw nameClashError(resourceType, resolvedName);
|
|
4255
|
+
}
|
|
4256
|
+
const managedName = findManagedNameById(stateManager, resourceType, id);
|
|
4257
|
+
if (!update && managedName !== void 0 && !namesEqual(managedName, resolvedName)) {
|
|
4258
|
+
throw new Error(
|
|
4259
|
+
`This ${resourceType} (id ${id}) is already managed by this project under the name "${managedName}", but is now named "${resolvedName}" on the platform.`
|
|
4260
|
+
);
|
|
4261
|
+
}
|
|
4262
|
+
return managedName;
|
|
4263
|
+
}
|
|
4155
4264
|
function writeResourceFiles(rootDir, resourceType, resource, apiResponse) {
|
|
4156
4265
|
const safeName = sanitizeFileName(resource.name);
|
|
4266
|
+
const target = resource;
|
|
4157
4267
|
if (resourceType === "workflow") {
|
|
4158
4268
|
const workflow = apiResponse;
|
|
4159
4269
|
const workflowResource = resource;
|
|
4160
4270
|
if (workflow.yaml_config) {
|
|
4161
4271
|
const defFileName = `${safeName}.yaml`;
|
|
4162
|
-
const defPath =
|
|
4272
|
+
const defPath = path16.join(rootDir, "workflows", defFileName);
|
|
4163
4273
|
ensureDirectoryExists(defPath);
|
|
4164
|
-
|
|
4274
|
+
fs11.writeFileSync(defPath, workflow.yaml_config, "utf8");
|
|
4165
4275
|
logger.info(` \u{1F4CB} Saved workflow definition: workflows/${defFileName}`);
|
|
4166
4276
|
workflowResource.definition = `workflows/${defFileName}`;
|
|
4277
|
+
} else {
|
|
4278
|
+
delete target.definition;
|
|
4167
4279
|
}
|
|
4168
|
-
const configPath =
|
|
4280
|
+
const configPath = path16.join(rootDir, "configs", "workflows", `${safeName}.yaml`);
|
|
4169
4281
|
const relativeConfigPath = `configs/workflows/${safeName}.yaml`;
|
|
4170
4282
|
ensureDirectoryExists(configPath);
|
|
4171
|
-
const yamlContent2 =
|
|
4172
|
-
|
|
4283
|
+
const yamlContent2 = yaml6.stringify(workflowResource, { lineWidth: 120 });
|
|
4284
|
+
fs11.writeFileSync(configPath, yamlContent2, "utf8");
|
|
4173
4285
|
logger.info(` \u{1F4C4} Saved workflow config: ${relativeConfigPath}`);
|
|
4174
4286
|
return relativeConfigPath;
|
|
4175
4287
|
}
|
|
4176
|
-
const pluralType = resourceType
|
|
4177
|
-
const resourceDir =
|
|
4178
|
-
const resourceFilePath =
|
|
4288
|
+
const pluralType = pluralTypeOf(resourceType);
|
|
4289
|
+
const resourceDir = path16.join(rootDir, "configs", "imported-resources", pluralType);
|
|
4290
|
+
const resourceFilePath = path16.join(resourceDir, `${safeName}.yaml`);
|
|
4179
4291
|
const relativeResourcePath = `configs/imported-resources/${pluralType}/${safeName}.yaml`;
|
|
4180
4292
|
if (resourceType === "assistant") {
|
|
4181
4293
|
const assistant = apiResponse;
|
|
4182
4294
|
const assistantResource = resource;
|
|
4183
4295
|
if (assistant.system_prompt) {
|
|
4184
4296
|
const promptFileName = assistant.slug || safeName;
|
|
4185
|
-
const promptPath =
|
|
4297
|
+
const promptPath = path16.join(rootDir, "system_prompts", `${promptFileName}.prompt.md`);
|
|
4186
4298
|
ensureDirectoryExists(promptPath);
|
|
4187
|
-
|
|
4299
|
+
fs11.writeFileSync(promptPath, assistant.system_prompt, "utf8");
|
|
4188
4300
|
logger.info(` \u{1F4DD} Saved prompt: system_prompts/${promptFileName}.prompt.md`);
|
|
4189
4301
|
assistantResource.prompt = `system_prompts/${promptFileName}.prompt.md`;
|
|
4302
|
+
} else {
|
|
4303
|
+
delete target.prompt;
|
|
4190
4304
|
}
|
|
4191
4305
|
} else if (resourceType === "skill") {
|
|
4192
4306
|
const skill = apiResponse;
|
|
4193
4307
|
const skillResource = resource;
|
|
4194
4308
|
if (skill.content) {
|
|
4195
4309
|
const skillFileName = `${safeName}.skill.md`;
|
|
4196
|
-
const skillPath =
|
|
4310
|
+
const skillPath = path16.join(rootDir, "skills", skillFileName);
|
|
4197
4311
|
ensureDirectoryExists(skillPath);
|
|
4198
|
-
|
|
4312
|
+
fs11.writeFileSync(skillPath, skill.content, "utf8");
|
|
4199
4313
|
logger.info(` \u{1F9E0} Saved skill content: skills/${skillFileName}`);
|
|
4200
4314
|
skillResource.file = `skills/${skillFileName}`;
|
|
4315
|
+
} else {
|
|
4316
|
+
delete target.file;
|
|
4201
4317
|
}
|
|
4202
4318
|
}
|
|
4203
4319
|
ensureDirectoryExists(resourceFilePath);
|
|
4204
|
-
const yamlContent =
|
|
4205
|
-
|
|
4320
|
+
const yamlContent = yaml6.stringify(resource, { lineWidth: 120 });
|
|
4321
|
+
fs11.writeFileSync(resourceFilePath, yamlContent, "utf8");
|
|
4206
4322
|
logger.info(` \u{1F4C4} Saved resource config: ${relativeResourcePath}`);
|
|
4207
4323
|
return relativeResourcePath;
|
|
4208
4324
|
}
|
|
4325
|
+
function planCompanionWrite(rootDir, target, key, configuredValue, content, defaultPath, label) {
|
|
4326
|
+
const configuredPath = typeof configuredValue === "string" ? configuredValue : void 0;
|
|
4327
|
+
if (!content) {
|
|
4328
|
+
if (configuredPath === void 0) {
|
|
4329
|
+
delete target[key];
|
|
4330
|
+
} else {
|
|
4331
|
+
target[key] = configuredPath;
|
|
4332
|
+
}
|
|
4333
|
+
return void 0;
|
|
4334
|
+
}
|
|
4335
|
+
const relPath = configuredPath ?? defaultPath;
|
|
4336
|
+
target[key] = relPath;
|
|
4337
|
+
return { absPath: path16.join(rootDir, relPath), content, log: ` ${label}: ${relPath}` };
|
|
4338
|
+
}
|
|
4339
|
+
var IN_LIST_ITEM_INDENT = 2;
|
|
4340
|
+
function itemCarriesAnchorAliasOrMergeKey(node) {
|
|
4341
|
+
if (node.anchor !== void 0) {
|
|
4342
|
+
return true;
|
|
4343
|
+
}
|
|
4344
|
+
return node.items.some((pair) => {
|
|
4345
|
+
const key = pair.key;
|
|
4346
|
+
const keyValue = yaml6.isScalar(key) ? key.value : key;
|
|
4347
|
+
if (keyValue === "<<") {
|
|
4348
|
+
return true;
|
|
4349
|
+
}
|
|
4350
|
+
const value = pair.value;
|
|
4351
|
+
if (yaml6.isAlias(value)) {
|
|
4352
|
+
return true;
|
|
4353
|
+
}
|
|
4354
|
+
return yaml6.isNode(value) && value.anchor !== void 0;
|
|
4355
|
+
});
|
|
4356
|
+
}
|
|
4357
|
+
function spliceResourceIntoList(filePath, relativeResourcePath, resource, existingName) {
|
|
4358
|
+
const source = fs11.readFileSync(filePath, "utf8");
|
|
4359
|
+
const doc = yaml6.parseDocument(source);
|
|
4360
|
+
const refusal = (reason) => new Error(
|
|
4361
|
+
`Cannot safely update "${existingName}" inside ${relativeResourcePath}: ${reason}. Update ${relativeResourcePath} manually.`
|
|
4362
|
+
);
|
|
4363
|
+
if (!yaml6.isSeq(doc.contents) || doc.contents.flow) {
|
|
4364
|
+
throw refusal("the file is not a block-style YAML sequence");
|
|
4365
|
+
}
|
|
4366
|
+
const node = doc.contents.items.find((item) => {
|
|
4367
|
+
if (!yaml6.isMap(item)) {
|
|
4368
|
+
return false;
|
|
4369
|
+
}
|
|
4370
|
+
const itemName = item.get("name");
|
|
4371
|
+
return typeof itemName === "string" && namesEqual(itemName, existingName);
|
|
4372
|
+
});
|
|
4373
|
+
if (!node?.range) {
|
|
4374
|
+
throw refusal("its entry could not be located as a block mapping in the sequence");
|
|
4375
|
+
}
|
|
4376
|
+
if (node.flow) {
|
|
4377
|
+
throw refusal("its entry uses flow style ({ ... })");
|
|
4378
|
+
}
|
|
4379
|
+
if (itemCarriesAnchorAliasOrMergeKey(node)) {
|
|
4380
|
+
throw refusal("its entry uses a YAML anchor, alias or merge key");
|
|
4381
|
+
}
|
|
4382
|
+
const [start, valueEnd] = node.range;
|
|
4383
|
+
const indent = start - (source.lastIndexOf("\n", start - 1) + 1);
|
|
4384
|
+
if (indent !== IN_LIST_ITEM_INDENT) {
|
|
4385
|
+
throw refusal(`its entry is indented by ${indent} spaces instead of ${IN_LIST_ITEM_INDENT}`);
|
|
4386
|
+
}
|
|
4387
|
+
const rendered = yaml6.stringify(resource, { lineWidth: 120 }).trimEnd();
|
|
4388
|
+
const replacement = rendered.split("\n").map((line, index) => index === 0 || !line ? line : `${" ".repeat(IN_LIST_ITEM_INDENT)}${line}`).join("\n");
|
|
4389
|
+
const tail = source.slice(valueEnd);
|
|
4390
|
+
const separator = tail.startsWith("\n") ? "" : "\n";
|
|
4391
|
+
return source.slice(0, start) + replacement + separator + tail;
|
|
4392
|
+
}
|
|
4393
|
+
function updateResourceFiles(rootDir, resourceType, resource, apiResponse, existing) {
|
|
4394
|
+
const filePath = existing.file;
|
|
4395
|
+
if (!filePath) {
|
|
4396
|
+
throw new Error("updateResourceFiles requires an existing resource backed by a file");
|
|
4397
|
+
}
|
|
4398
|
+
const existingResource = existing.resource;
|
|
4399
|
+
const target = resource;
|
|
4400
|
+
const safeName = sanitizeFileName(resource.name);
|
|
4401
|
+
for (const [key, value] of Object.entries(existingResource)) {
|
|
4402
|
+
if (key === "skill_ids" || key in target) {
|
|
4403
|
+
continue;
|
|
4404
|
+
}
|
|
4405
|
+
target[key] = value;
|
|
4406
|
+
}
|
|
4407
|
+
let companion;
|
|
4408
|
+
if (resourceType === "assistant") {
|
|
4409
|
+
const assistant = apiResponse;
|
|
4410
|
+
companion = planCompanionWrite(
|
|
4411
|
+
rootDir,
|
|
4412
|
+
target,
|
|
4413
|
+
"prompt",
|
|
4414
|
+
existingResource.prompt,
|
|
4415
|
+
assistant.system_prompt,
|
|
4416
|
+
`system_prompts/${assistant.slug || safeName}.prompt.md`,
|
|
4417
|
+
"\u{1F4DD} Updated prompt"
|
|
4418
|
+
);
|
|
4419
|
+
} else if (resourceType === "skill") {
|
|
4420
|
+
const skill = apiResponse;
|
|
4421
|
+
companion = planCompanionWrite(
|
|
4422
|
+
rootDir,
|
|
4423
|
+
target,
|
|
4424
|
+
"file",
|
|
4425
|
+
existingResource.file,
|
|
4426
|
+
skill.content,
|
|
4427
|
+
`skills/${safeName}.skill.md`,
|
|
4428
|
+
"\u{1F9E0} Updated skill content"
|
|
4429
|
+
);
|
|
4430
|
+
} else if (resourceType === "workflow") {
|
|
4431
|
+
const workflow = apiResponse;
|
|
4432
|
+
companion = planCompanionWrite(
|
|
4433
|
+
rootDir,
|
|
4434
|
+
target,
|
|
4435
|
+
"definition",
|
|
4436
|
+
existingResource.definition,
|
|
4437
|
+
workflow.yaml_config,
|
|
4438
|
+
`workflows/${safeName}.yaml`,
|
|
4439
|
+
"\u{1F4CB} Updated workflow definition"
|
|
4440
|
+
);
|
|
4441
|
+
}
|
|
4442
|
+
const relativeResourcePath = path16.relative(rootDir, filePath);
|
|
4443
|
+
const newResourceFileContent = existing.inList ? spliceResourceIntoList(filePath, relativeResourcePath, resource, String(existingResource.name)) : yaml6.stringify(resource, { lineWidth: 120 });
|
|
4444
|
+
if (companion) {
|
|
4445
|
+
ensureDirectoryExists(companion.absPath);
|
|
4446
|
+
fs11.writeFileSync(companion.absPath, companion.content, "utf8");
|
|
4447
|
+
logger.info(companion.log);
|
|
4448
|
+
}
|
|
4449
|
+
fs11.writeFileSync(filePath, newResourceFileContent, "utf8");
|
|
4450
|
+
logger.info(` \u{1F4C4} Updated resource config: ${relativeResourcePath}`);
|
|
4451
|
+
return relativeResourcePath;
|
|
4452
|
+
}
|
|
4453
|
+
function deleteStateEntry(stateManager, resourceType, name) {
|
|
4454
|
+
switch (resourceType) {
|
|
4455
|
+
case "assistant": {
|
|
4456
|
+
stateManager.deleteAssistantState(name);
|
|
4457
|
+
break;
|
|
4458
|
+
}
|
|
4459
|
+
case "datasource": {
|
|
4460
|
+
stateManager.deleteDatasourceState(name);
|
|
4461
|
+
break;
|
|
4462
|
+
}
|
|
4463
|
+
case "workflow": {
|
|
4464
|
+
stateManager.deleteWorkflowState(name);
|
|
4465
|
+
break;
|
|
4466
|
+
}
|
|
4467
|
+
case "skill": {
|
|
4468
|
+
stateManager.deleteSkillState(name);
|
|
4469
|
+
break;
|
|
4470
|
+
}
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4209
4473
|
function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relativeResourcePath) {
|
|
4210
|
-
const configPath =
|
|
4211
|
-
if (!
|
|
4474
|
+
const configPath = path16.join(rootDir, codemieConfigPath);
|
|
4475
|
+
if (!fs11.existsSync(configPath)) {
|
|
4212
4476
|
throw new Error(`Configuration file not found: ${configPath}`);
|
|
4213
4477
|
}
|
|
4214
|
-
const content =
|
|
4215
|
-
const doc =
|
|
4216
|
-
const pluralType = resourceType
|
|
4478
|
+
const content = fs11.readFileSync(configPath, "utf8");
|
|
4479
|
+
const doc = yaml6.parseDocument(content);
|
|
4480
|
+
const pluralType = pluralTypeOf(resourceType);
|
|
4217
4481
|
let resourcesNode = doc.get("resources");
|
|
4218
4482
|
if (!resourcesNode) {
|
|
4219
4483
|
resourcesNode = doc.createNode({});
|
|
@@ -4226,7 +4490,7 @@ function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relati
|
|
|
4226
4490
|
}
|
|
4227
4491
|
const importDirective = { $import: relativeResourcePath };
|
|
4228
4492
|
const existingImports = typeArray.items.filter((item) => {
|
|
4229
|
-
if (
|
|
4493
|
+
if (yaml6.isMap(item)) {
|
|
4230
4494
|
const importValue = item.get("$import");
|
|
4231
4495
|
return importValue === relativeResourcePath;
|
|
4232
4496
|
}
|
|
@@ -4238,16 +4502,16 @@ function addImportToCodemieYaml(rootDir, codemieConfigPath, resourceType, relati
|
|
|
4238
4502
|
}
|
|
4239
4503
|
const newNode = doc.createNode(importDirective);
|
|
4240
4504
|
typeArray.add(newNode);
|
|
4241
|
-
|
|
4505
|
+
fs11.writeFileSync(configPath, doc.toString(), "utf8");
|
|
4242
4506
|
logger.info(` \u2705 Added $import to ${codemieConfigPath} \u2192 resources.${pluralType}`);
|
|
4243
4507
|
}
|
|
4244
4508
|
function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
|
|
4245
|
-
const configPath =
|
|
4246
|
-
if (!
|
|
4509
|
+
const configPath = path16.join(rootDir, codemieConfigPath);
|
|
4510
|
+
if (!fs11.existsSync(configPath)) {
|
|
4247
4511
|
throw new Error(`Configuration file not found: ${configPath}`);
|
|
4248
4512
|
}
|
|
4249
|
-
const content =
|
|
4250
|
-
const doc =
|
|
4513
|
+
const content = fs11.readFileSync(configPath, "utf8");
|
|
4514
|
+
const doc = yaml6.parseDocument(content);
|
|
4251
4515
|
let importedNode = doc.get("imported");
|
|
4252
4516
|
if (!importedNode) {
|
|
4253
4517
|
importedNode = doc.createNode({ assistants: [], datasources: [], skills: [], integrations: [] });
|
|
@@ -4259,7 +4523,7 @@ function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
|
|
|
4259
4523
|
doc.setIn(["imported", "skills"], skillsArray);
|
|
4260
4524
|
}
|
|
4261
4525
|
const existingSkill = skillsArray.items.find((item) => {
|
|
4262
|
-
if (!
|
|
4526
|
+
if (!yaml6.isMap(item)) {
|
|
4263
4527
|
return false;
|
|
4264
4528
|
}
|
|
4265
4529
|
const existingName = item.get("name");
|
|
@@ -4276,9 +4540,47 @@ function addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill) {
|
|
|
4276
4540
|
return;
|
|
4277
4541
|
}
|
|
4278
4542
|
skillsArray.add(doc.createNode({ name: skill.name, id: skill.id }));
|
|
4279
|
-
|
|
4543
|
+
fs11.writeFileSync(configPath, doc.toString(), "utf8");
|
|
4280
4544
|
logger.info(` \u2705 Added skill to ${codemieConfigPath} \u2192 imported.skills`);
|
|
4281
4545
|
}
|
|
4546
|
+
function updateImportedSkillInCodemieYaml(rootDir, codemieConfigPath, skill) {
|
|
4547
|
+
const configPath = path16.join(rootDir, codemieConfigPath);
|
|
4548
|
+
if (!fs11.existsSync(configPath)) {
|
|
4549
|
+
throw new Error(`Configuration file not found: ${configPath}`);
|
|
4550
|
+
}
|
|
4551
|
+
const content = fs11.readFileSync(configPath, "utf8");
|
|
4552
|
+
const doc = yaml6.parseDocument(content);
|
|
4553
|
+
const skillsArray = doc.getIn(["imported", "skills"]);
|
|
4554
|
+
if (!skillsArray) {
|
|
4555
|
+
return false;
|
|
4556
|
+
}
|
|
4557
|
+
const items = skillsArray.items.filter((item) => yaml6.isMap(item));
|
|
4558
|
+
const entryById = items.find((item) => {
|
|
4559
|
+
const existingId = item.get("id");
|
|
4560
|
+
return typeof existingId === "string" && existingId === skill.id;
|
|
4561
|
+
});
|
|
4562
|
+
const entry = entryById ?? items.find((item) => {
|
|
4563
|
+
const existingName = item.get("name");
|
|
4564
|
+
return typeof existingName === "string" && namesEqual(existingName, skill.name);
|
|
4565
|
+
});
|
|
4566
|
+
if (!entry) {
|
|
4567
|
+
return false;
|
|
4568
|
+
}
|
|
4569
|
+
entry.set("name", skill.name);
|
|
4570
|
+
entry.set("id", skill.id);
|
|
4571
|
+
fs11.writeFileSync(configPath, doc.toString(), "utf8");
|
|
4572
|
+
return true;
|
|
4573
|
+
}
|
|
4574
|
+
function upsertImportedSkill(rootDir, codemieConfigPath, skill, update) {
|
|
4575
|
+
if (update) {
|
|
4576
|
+
if (updateImportedSkillInCodemieYaml(rootDir, codemieConfigPath, skill)) {
|
|
4577
|
+
logger.info(` \u267B\uFE0F Refreshed imported.skills entry for external skill "${skill.name}"`);
|
|
4578
|
+
return;
|
|
4579
|
+
}
|
|
4580
|
+
logger.info(` No existing skill found in the configuration \u2014 importing as new`);
|
|
4581
|
+
}
|
|
4582
|
+
addImportedSkillToCodemieYaml(rootDir, codemieConfigPath, skill);
|
|
4583
|
+
}
|
|
4282
4584
|
function ensureExternalSkillDoesNotConflictWithManagedSkill(config, skillName) {
|
|
4283
4585
|
const managedSkill = config.resources.skills?.find((skill) => namesEqual(skill.name, skillName));
|
|
4284
4586
|
if (!managedSkill) {
|
|
@@ -4331,7 +4633,7 @@ function buildIntegrationAliasMap(config) {
|
|
|
4331
4633
|
return map;
|
|
4332
4634
|
}
|
|
4333
4635
|
async function importResource(options) {
|
|
4334
|
-
const { appConfig, resourceType, slug } = options;
|
|
4636
|
+
const { appConfig, resourceType, slug, update = false } = options;
|
|
4335
4637
|
if (!VALID_RESOURCE_TYPES.includes(resourceType)) {
|
|
4336
4638
|
throw new Error(`Invalid resource type: "${resourceType}". Must be one of: ${VALID_RESOURCE_TYPES.join(", ")}`);
|
|
4337
4639
|
}
|
|
@@ -4339,10 +4641,8 @@ async function importResource(options) {
|
|
|
4339
4641
|
const config = configLoader.loadConfig();
|
|
4340
4642
|
const rootDir = appConfig.rootDir;
|
|
4341
4643
|
const stateManager = new StateManager(appConfig);
|
|
4342
|
-
if (checkResourceExists2(config, resourceType, slug)) {
|
|
4343
|
-
throw
|
|
4344
|
-
`A ${resourceType} named "${slug}" already exists in the configuration. Remove it first or use a different name.`
|
|
4345
|
-
);
|
|
4644
|
+
if (!update && checkResourceExists2(config, resourceType, slug)) {
|
|
4645
|
+
throw nameClashError(resourceType, slug);
|
|
4346
4646
|
}
|
|
4347
4647
|
logger.info(`
|
|
4348
4648
|
\u{1F50D} Importing ${resourceType}: "${slug}"
|
|
@@ -4352,11 +4652,21 @@ async function importResource(options) {
|
|
|
4352
4652
|
const integrationAliasMap = buildIntegrationAliasMap(config);
|
|
4353
4653
|
let resource;
|
|
4354
4654
|
let apiResponse;
|
|
4655
|
+
let managedName;
|
|
4355
4656
|
switch (resourceType) {
|
|
4356
4657
|
case "assistant": {
|
|
4357
4658
|
logger.info(` Searching for assistant "${slug}"...`);
|
|
4358
4659
|
const assistant = await findAssistant(client, slug);
|
|
4359
4660
|
logger.info(` \u2713 Found: ${assistant.name} (${assistant.id})`);
|
|
4661
|
+
managedName = assertResolvedNameIsFree(
|
|
4662
|
+
config,
|
|
4663
|
+
stateManager,
|
|
4664
|
+
resourceType,
|
|
4665
|
+
slug,
|
|
4666
|
+
assistant.name,
|
|
4667
|
+
assistant.id,
|
|
4668
|
+
update
|
|
4669
|
+
);
|
|
4360
4670
|
apiResponse = assistant;
|
|
4361
4671
|
resource = assistantResponseToResource(assistant);
|
|
4362
4672
|
if (integrationAliasMap.size > 0) {
|
|
@@ -4390,7 +4700,7 @@ async function importResource(options) {
|
|
|
4390
4700
|
logger.info(` \u2713 Resolved skill ${skillId.slice(0, 8)}... \u2192 "${skill.name}"`);
|
|
4391
4701
|
if (!isProjectOwnedSkill(skill, config.project.name)) {
|
|
4392
4702
|
ensureExternalSkillDoesNotConflictWithManagedSkill(config, skill.name);
|
|
4393
|
-
|
|
4703
|
+
upsertImportedSkill(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id }, update);
|
|
4394
4704
|
}
|
|
4395
4705
|
}
|
|
4396
4706
|
if (skillNames.length > 0) {
|
|
@@ -4403,6 +4713,15 @@ async function importResource(options) {
|
|
|
4403
4713
|
logger.info(` Searching for datasource "${slug}"...`);
|
|
4404
4714
|
const datasource = await findDatasource(client, slug);
|
|
4405
4715
|
logger.info(` \u2713 Found: ${datasource.name} (${datasource.id})`);
|
|
4716
|
+
managedName = assertResolvedNameIsFree(
|
|
4717
|
+
config,
|
|
4718
|
+
stateManager,
|
|
4719
|
+
resourceType,
|
|
4720
|
+
slug,
|
|
4721
|
+
datasource.name,
|
|
4722
|
+
datasource.id,
|
|
4723
|
+
update
|
|
4724
|
+
);
|
|
4406
4725
|
apiResponse = datasource;
|
|
4407
4726
|
const settingId = datasource.setting_id || "";
|
|
4408
4727
|
const integrationAlias = integrationAliasMap.get(settingId);
|
|
@@ -4413,11 +4732,15 @@ async function importResource(options) {
|
|
|
4413
4732
|
logger.info(` Searching for workflow "${slug}"...`);
|
|
4414
4733
|
const workflow = await findWorkflow(client, slug);
|
|
4415
4734
|
logger.info(` \u2713 Found: ${workflow.name} (${workflow.id})`);
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
4420
|
-
|
|
4735
|
+
managedName = assertResolvedNameIsFree(
|
|
4736
|
+
config,
|
|
4737
|
+
stateManager,
|
|
4738
|
+
resourceType,
|
|
4739
|
+
slug,
|
|
4740
|
+
workflow.name,
|
|
4741
|
+
workflow.id,
|
|
4742
|
+
update
|
|
4743
|
+
);
|
|
4421
4744
|
apiResponse = workflow;
|
|
4422
4745
|
resource = workflowResponseToResource(workflow);
|
|
4423
4746
|
break;
|
|
@@ -4426,24 +4749,44 @@ async function importResource(options) {
|
|
|
4426
4749
|
logger.info(` Searching for skill "${slug}"...`);
|
|
4427
4750
|
const skill = await findSkill(client, slug);
|
|
4428
4751
|
logger.info(` \u2713 Found: ${skill.name} (${skill.id})`);
|
|
4429
|
-
|
|
4430
|
-
throw new Error(
|
|
4431
|
-
`A ${resourceType} named "${skill.name}" already exists in the configuration. Remove it first or use a different name.`
|
|
4432
|
-
);
|
|
4433
|
-
}
|
|
4752
|
+
managedName = assertResolvedNameIsFree(config, stateManager, resourceType, slug, skill.name, skill.id, update);
|
|
4434
4753
|
if (isProjectOwnedSkill(skill, config.project.name)) {
|
|
4435
4754
|
logger.info(` Project-owned skill \u2014 importing into resources.skills`);
|
|
4436
4755
|
apiResponse = skill;
|
|
4437
4756
|
resource = skillResponseToResource(skill);
|
|
4438
4757
|
break;
|
|
4439
4758
|
}
|
|
4440
|
-
|
|
4759
|
+
upsertImportedSkill(rootDir, appConfig.codemieConfig, { name: skill.name, id: skill.id }, update);
|
|
4441
4760
|
logger.info(`
|
|
4442
4761
|
\u2705 Successfully imported external skill "${skill.name}" into imported.skills
|
|
4443
4762
|
`);
|
|
4444
4763
|
return;
|
|
4445
4764
|
}
|
|
4446
4765
|
}
|
|
4766
|
+
if (update) {
|
|
4767
|
+
const pluralType = pluralTypeOf(resourceType);
|
|
4768
|
+
const lookupName = managedName ?? resource.name;
|
|
4769
|
+
const existing = findResourceDefinition(rootDir, appConfig.codemieConfig, pluralType, lookupName);
|
|
4770
|
+
if (existing) {
|
|
4771
|
+
if (existing.file === null) {
|
|
4772
|
+
throw new Error(
|
|
4773
|
+
`Cannot update "${resource.name}": it is not defined in a file referenced by codemie.yaml. Update it manually.`
|
|
4774
|
+
);
|
|
4775
|
+
}
|
|
4776
|
+
logger.info(` \u267B\uFE0F Updating existing ${resourceType} config in place...`);
|
|
4777
|
+
updateResourceFiles(rootDir, resourceType, resource, apiResponse, existing);
|
|
4778
|
+
if (managedName !== void 0 && !namesEqual(managedName, resource.name)) {
|
|
4779
|
+
logger.info(` \u{1F3F7}\uFE0F Renamed from "${managedName}" to "${resource.name}" \u2014 updating state.json`);
|
|
4780
|
+
deleteStateEntry(stateManager, resourceType, managedName);
|
|
4781
|
+
}
|
|
4782
|
+
updateState(stateManager, resourceType, resource, apiResponse);
|
|
4783
|
+
logger.info(`
|
|
4784
|
+
\u2705 Successfully imported ${resourceType} "${resource.name}"
|
|
4785
|
+
`);
|
|
4786
|
+
return;
|
|
4787
|
+
}
|
|
4788
|
+
logger.info(` No existing ${resourceType} found in the configuration \u2014 importing as new`);
|
|
4789
|
+
}
|
|
4447
4790
|
const relativeResourcePath = writeResourceFiles(rootDir, resourceType, resource, apiResponse);
|
|
4448
4791
|
addImportToCodemieYaml(rootDir, appConfig.codemieConfig, resourceType, relativeResourcePath);
|
|
4449
4792
|
updateState(stateManager, resourceType, resource, apiResponse);
|
|
@@ -4464,7 +4807,7 @@ async function main4(options) {
|
|
|
4464
4807
|
}
|
|
4465
4808
|
init_logger();
|
|
4466
4809
|
function fileExists(filePath) {
|
|
4467
|
-
return
|
|
4810
|
+
return fs11.existsSync(filePath);
|
|
4468
4811
|
}
|
|
4469
4812
|
function generateEnvTemplate() {
|
|
4470
4813
|
return `# Codemie API Configuration
|
|
@@ -4543,14 +4886,14 @@ function compareConfigWithDefaults(userConfig) {
|
|
|
4543
4886
|
}
|
|
4544
4887
|
async function saveConfigFile(config, defaultFilename = "config.json") {
|
|
4545
4888
|
const configFilename = await promptUser(`Enter config file path`, defaultFilename);
|
|
4546
|
-
const configPath =
|
|
4889
|
+
const configPath = path16.resolve(process.cwd(), configFilename);
|
|
4547
4890
|
if (fileExists(configPath)) {
|
|
4548
4891
|
const shouldOverwrite = await confirmOverwrite(configFilename);
|
|
4549
4892
|
if (!shouldOverwrite) {
|
|
4550
4893
|
throw new Error(`Cancelled: Configuration file not saved.`);
|
|
4551
4894
|
}
|
|
4552
4895
|
}
|
|
4553
|
-
|
|
4896
|
+
fs11.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
4554
4897
|
return configFilename;
|
|
4555
4898
|
}
|
|
4556
4899
|
function suggestConfigUsage(configFileName) {
|
|
@@ -4569,8 +4912,8 @@ async function initProject(options) {
|
|
|
4569
4912
|
logger.info("\u{1F680} Initialize Codemie IaC Project\n\n");
|
|
4570
4913
|
try {
|
|
4571
4914
|
const { appConfig } = options;
|
|
4572
|
-
const envPath =
|
|
4573
|
-
const configPath =
|
|
4915
|
+
const envPath = path16.resolve(appConfig.rootDir, ".env");
|
|
4916
|
+
const configPath = path16.resolve(appConfig.rootDir, appConfig.codemieConfig);
|
|
4574
4917
|
logger.info("This wizard will create the following files:");
|
|
4575
4918
|
logger.info(` \u{1F4C4} .env - Environment variables with API credentials`);
|
|
4576
4919
|
logger.info(` \u{1F4C4} ${appConfig.codemieConfig} - Codemie IaC configuration
|
|
@@ -4623,7 +4966,7 @@ async function initProject(options) {
|
|
|
4623
4966
|
logger.info("\n\u{1F4C1} Creating configuration files...\n");
|
|
4624
4967
|
if (shouldCreateEnv) {
|
|
4625
4968
|
const envContent = generateEnvTemplate();
|
|
4626
|
-
|
|
4969
|
+
fs11.writeFileSync(envPath, envContent, "utf-8");
|
|
4627
4970
|
logger.info(`\u2713 Created .env`);
|
|
4628
4971
|
logger.warn(` \u26A0\uFE0F Remember to fill in your actual credentials!`);
|
|
4629
4972
|
logger.warn(` \u26A0\uFE0F DO NOT commit .env to Git!
|
|
@@ -4631,7 +4974,7 @@ async function initProject(options) {
|
|
|
4631
4974
|
}
|
|
4632
4975
|
if (shouldCreateConfig) {
|
|
4633
4976
|
const configContent = generateMinimalCodemieYaml(projectName, projectDescription);
|
|
4634
|
-
|
|
4977
|
+
fs11.writeFileSync(configPath, configContent, "utf-8");
|
|
4635
4978
|
logger.info(`\u2713 Created ${appConfig.codemieConfig}`);
|
|
4636
4979
|
logger.info(` See examples/ directory for detailed configuration examples
|
|
4637
4980
|
|
|
@@ -4665,6 +5008,7 @@ async function main5(options) {
|
|
|
4665
5008
|
// src/cli/index.ts
|
|
4666
5009
|
init_commandSpinner();
|
|
4667
5010
|
init_logger();
|
|
5011
|
+
init_logger();
|
|
4668
5012
|
|
|
4669
5013
|
// src/lib/previewUtils.ts
|
|
4670
5014
|
init_checksumUtils();
|
|
@@ -4895,11 +5239,11 @@ async function previewWorkflows(workflows, stateManager, client, rootDir = proce
|
|
|
4895
5239
|
const projectClause = project ? ` in project ${project}` : "";
|
|
4896
5240
|
const staleDetails = `ID from state not found; will adopt a same-name workflow${projectClause} if one exists, otherwise create`;
|
|
4897
5241
|
for (const workflow of workflows) {
|
|
4898
|
-
const yamlPath =
|
|
4899
|
-
if (!
|
|
5242
|
+
const yamlPath = path16.join(rootDir, workflow.definition);
|
|
5243
|
+
if (!fs11.existsSync(yamlPath)) {
|
|
4900
5244
|
throw new Error(`Workflow definition file not found: ${workflow.definition}`);
|
|
4901
5245
|
}
|
|
4902
|
-
const yamlConfig =
|
|
5246
|
+
const yamlConfig = fs11.readFileSync(yamlPath, "utf8");
|
|
4903
5247
|
const change = await previewResource(
|
|
4904
5248
|
workflow,
|
|
4905
5249
|
"workflow",
|
|
@@ -5087,10 +5431,10 @@ var DependencyValidator = class {
|
|
|
5087
5431
|
const errors = [];
|
|
5088
5432
|
const visited = /* @__PURE__ */ new Set();
|
|
5089
5433
|
const recursionStack = /* @__PURE__ */ new Set();
|
|
5090
|
-
const dfs = (name,
|
|
5434
|
+
const dfs = (name, path17) => {
|
|
5091
5435
|
if (recursionStack.has(name)) {
|
|
5092
|
-
const cycleStart =
|
|
5093
|
-
const cycle = [...
|
|
5436
|
+
const cycleStart = path17.indexOf(name);
|
|
5437
|
+
const cycle = [...path17.slice(cycleStart), name];
|
|
5094
5438
|
errors.push(`Cyclic dependency detected: ${cycle.join(" \u2192 ")}`);
|
|
5095
5439
|
return true;
|
|
5096
5440
|
}
|
|
@@ -5099,14 +5443,14 @@ var DependencyValidator = class {
|
|
|
5099
5443
|
}
|
|
5100
5444
|
visited.add(name);
|
|
5101
5445
|
recursionStack.add(name);
|
|
5102
|
-
|
|
5446
|
+
path17.push(name);
|
|
5103
5447
|
const assistant = assistantMap.get(name);
|
|
5104
5448
|
const subAssistantRefs = assistant?.sub_assistants || [];
|
|
5105
5449
|
for (const subRef of subAssistantRefs) {
|
|
5106
5450
|
if (!assistantMap.has(subRef)) {
|
|
5107
5451
|
continue;
|
|
5108
5452
|
}
|
|
5109
|
-
if (dfs(subRef, [...
|
|
5453
|
+
if (dfs(subRef, [...path17])) {
|
|
5110
5454
|
return true;
|
|
5111
5455
|
}
|
|
5112
5456
|
}
|
|
@@ -5165,7 +5509,7 @@ var DependencyValidator = class {
|
|
|
5165
5509
|
}
|
|
5166
5510
|
for (const workflow of workflows) {
|
|
5167
5511
|
try {
|
|
5168
|
-
const workflowYaml =
|
|
5512
|
+
const workflowYaml = yaml6.parse(workflow.definition);
|
|
5169
5513
|
if (!workflowYaml.assistants) {
|
|
5170
5514
|
continue;
|
|
5171
5515
|
}
|
|
@@ -5305,12 +5649,12 @@ async function validateConfig(options) {
|
|
|
5305
5649
|
}
|
|
5306
5650
|
if (config.resources.workflows && config.resources.workflows.length > 0) {
|
|
5307
5651
|
const workflowsWithContent = config.resources.workflows.map((workflow) => {
|
|
5308
|
-
const workflowPath =
|
|
5309
|
-
if (!
|
|
5652
|
+
const workflowPath = path16.resolve(workflow.definition);
|
|
5653
|
+
if (!fs11.existsSync(workflowPath)) {
|
|
5310
5654
|
errors.push(`Workflow definition file not found: ${workflow.definition}`);
|
|
5311
5655
|
return { ...workflow, definition: "" };
|
|
5312
5656
|
}
|
|
5313
|
-
const definition =
|
|
5657
|
+
const definition = fs11.readFileSync(workflowPath, "utf8");
|
|
5314
5658
|
return { ...workflow, definition };
|
|
5315
5659
|
});
|
|
5316
5660
|
const workflowErrors = DependencyValidator.validateWorkflowAssistantReferences(
|
|
@@ -5374,55 +5718,62 @@ async function main7(options) {
|
|
|
5374
5718
|
|
|
5375
5719
|
// src/cli/index.ts
|
|
5376
5720
|
program.name("codemie").description("Infrastructure as Code for Codemie platform").version(package_default.version);
|
|
5377
|
-
program.command("init").description("Initialize a new Codemie IaC project").action(() => {
|
|
5378
|
-
const
|
|
5721
|
+
program.command("init").description("Initialize a new Codemie IaC project").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").action((options) => {
|
|
5722
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5723
|
+
const appConfig = loadAppConfig(void 0, rootDir ?? process.cwd());
|
|
5379
5724
|
void main5({ appConfig });
|
|
5380
5725
|
});
|
|
5381
|
-
program.command("deploy").description("Deploy resources to Codemie platform").option("-p, --prune", "Delete orphaned resources").option("--skip-datasources", "Skip the datasource stage entirely (no create/update/delete, no indexing)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5382
|
-
const configPath = options.config ?
|
|
5383
|
-
const
|
|
5726
|
+
program.command("deploy").description("Deploy resources to Codemie platform").option("-p, --prune", "Delete orphaned resources").option("--skip-datasources", "Skip the datasource stage entirely (no create/update/delete, no indexing)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5727
|
+
const configPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5728
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5729
|
+
const appConfig = loadAppConfig(configPath, rootDir);
|
|
5384
5730
|
await runCommandWithSpinner(
|
|
5385
5731
|
"Deploying Codemie resources",
|
|
5386
5732
|
() => main2({ appConfig, prune: options.prune, skipDatasources: options.skipDatasources })
|
|
5387
5733
|
);
|
|
5388
5734
|
});
|
|
5389
|
-
program.command("backup").description("Backup all resources from Codemie platform").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5390
|
-
const configPath = options.config ?
|
|
5391
|
-
const
|
|
5735
|
+
program.command("backup").description("Backup all resources from Codemie platform").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5736
|
+
const configPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5737
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5738
|
+
const appConfig = loadAppConfig(configPath, rootDir);
|
|
5392
5739
|
await runCommandWithSpinner("Backing up Codemie resources", () => main({ appConfig }));
|
|
5393
5740
|
});
|
|
5394
|
-
program.command("destroy").description("Destroy all IaC-managed resources").option("-f, --force", "Skip confirmation prompt").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5395
|
-
const configPath = options.config ?
|
|
5396
|
-
const
|
|
5741
|
+
program.command("destroy").description("Destroy all IaC-managed resources").option("-f, --force", "Skip confirmation prompt").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5742
|
+
const configPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5743
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5744
|
+
const appConfig = loadAppConfig(configPath, rootDir);
|
|
5397
5745
|
await runCommandWithSpinner("Destroying Codemie resources", () => main3({ appConfig, force: options.force }));
|
|
5398
5746
|
});
|
|
5399
|
-
program.command("preview").description("Preview changes without applying them").option("--skip-datasources", "Exclude datasources from preview (mirrors deploy --skip-datasources)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5400
|
-
const appConfigPath = options.config ?
|
|
5401
|
-
const
|
|
5747
|
+
program.command("preview").description("Preview changes without applying them").option("--skip-datasources", "Exclude datasources from preview (mirrors deploy --skip-datasources)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5748
|
+
const appConfigPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5749
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5750
|
+
const appConfig = loadAppConfig(appConfigPath, rootDir);
|
|
5402
5751
|
await runCommandWithSpinner(
|
|
5403
5752
|
"Generating Codemie preview",
|
|
5404
5753
|
() => main6({ appConfig, skipDatasources: options.skipDatasources })
|
|
5405
5754
|
);
|
|
5406
5755
|
});
|
|
5407
|
-
program.command("validate").description("Validate configuration").option("-a, --check-api", "Check for API conflicts").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5408
|
-
const configPath = options.config ?
|
|
5409
|
-
const
|
|
5756
|
+
program.command("validate").description("Validate configuration").option("-a, --check-api", "Check for API conflicts").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").action(async (options) => {
|
|
5757
|
+
const configPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5758
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5759
|
+
const appConfig = loadAppConfig(configPath, rootDir);
|
|
5410
5760
|
await runCommandWithSpinner(
|
|
5411
5761
|
"Validating Codemie configuration",
|
|
5412
5762
|
() => main7({ appConfig, checkApi: options.checkApi })
|
|
5413
5763
|
);
|
|
5414
5764
|
});
|
|
5415
|
-
program.command("import").description("Import a single resource from the CodeMie platform into the local IaC configuration").argument("<type>", "Resource type: assistant, datasource, workflow, or skill").argument("<slug>", "Resource slug or name
|
|
5416
|
-
|
|
5417
|
-
|
|
5765
|
+
program.command("import").description("Import a single resource from the CodeMie platform into the local IaC configuration").argument("<type>", "Resource type: assistant, datasource, workflow, or skill").argument("<slug>", "Resource slug or name (a platform UUID also works for assistant and workflow)").option("-d, --root-dir <path>", "Project root directory (containing codemie.yaml)").option("-c, --config <path>", "Configuration file").option("-u, --update", "Refresh an already-managed resource in place instead of failing on a conflict").action(async (type, slug, options) => {
|
|
5766
|
+
if (!VALID_RESOURCE_TYPES.includes(type)) {
|
|
5767
|
+
logger.error(`\u274C Invalid resource type: "${type}". Must be one of: ${VALID_RESOURCE_TYPES.join(", ")}`);
|
|
5418
5768
|
process.exitCode = 1;
|
|
5419
5769
|
return;
|
|
5420
5770
|
}
|
|
5421
|
-
const configPath = options.config ?
|
|
5422
|
-
const
|
|
5771
|
+
const configPath = options.config ? path16__default.resolve(options.config) : void 0;
|
|
5772
|
+
const rootDir = options.rootDir ? path16__default.resolve(options.rootDir) : void 0;
|
|
5773
|
+
const appConfig = loadAppConfig(configPath, rootDir);
|
|
5423
5774
|
await runCommandWithSpinner(
|
|
5424
5775
|
`Importing ${type}`,
|
|
5425
|
-
() => main4({ appConfig, resourceType: type, slug })
|
|
5776
|
+
() => main4({ appConfig, resourceType: type, slug, update: options.update })
|
|
5426
5777
|
);
|
|
5427
5778
|
});
|
|
5428
5779
|
program.parse();
|