@codemieai/cdk 0.1.288 → 0.1.290

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 CHANGED
@@ -78,6 +78,9 @@ The `.env` file is already added to `.gitignore` and should never be committed t
78
78
  ## Quick Start
79
79
 
80
80
  ```bash
81
+ # Initialize a new Codemie IaC project (creates .env and codemie.yaml via wizard)
82
+ codemie init
83
+
81
84
  # Full validation with API connectivity check (default, recommended)
82
85
  npm run validate
83
86
 
@@ -104,6 +107,22 @@ npm run <command> --config codemie/iac.json
104
107
  ```
105
108
  **Note:** All commands work identically whether you use monolithic configuration or modular imports - the `$import` directives are automatically resolved during config loading.
106
109
 
110
+ ### Project Initialization (`codemie init`)
111
+
112
+ Use the interactive wizard to scaffold a project:
113
+
114
+ ```bash
115
+ codemie init
116
+ ```
117
+
118
+ What it does:
119
+ - Creates a `.env` template (with a safety prompt if it already exists)
120
+ - Generates a minimal `codemie.yaml` using your project name and description
121
+ - Saves an optional `appConfig` file when you pick non-default paths for config/state/backups
122
+ - Prints next steps to validate and deploy
123
+
124
+ If `.env` or `codemie.yaml` already exist, the wizard asks before overwriting and will keep your files if you decline.
125
+
107
126
  **Config file structure:**
108
127
  ```json
109
128
  {
package/dist/cli/index.js CHANGED
@@ -9,10 +9,11 @@ import * as crypto from 'crypto';
9
9
  import * as yaml3 from 'yaml';
10
10
  import { CodeMieClient, DataSourceType } from 'codemie-sdk';
11
11
  import pLimit from 'p-limit';
12
+ import * as readline from 'readline';
12
13
 
13
14
  // package.json
14
15
  var package_default = {
15
- version: "0.1.288"};
16
+ version: "0.1.290"};
16
17
  var appConfigSchema = z.object({
17
18
  rootDir: z.string(),
18
19
  codemieConfig: z.string(),
@@ -1275,28 +1276,28 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1275
1276
  * @param rootConfig - Root config object (constant reference for resolving paths like "imported.integrations.xxx")
1276
1277
  * @param path - Current path in config tree (for error messages, e.g., "resources.assistants[0].toolkits")
1277
1278
  */
1278
- resolveReferencesRecursive(current, rootConfig, path13 = "") {
1279
+ resolveReferencesRecursive(current, rootConfig, path15 = "") {
1279
1280
  if (!current || typeof current !== "object") {
1280
1281
  return;
1281
1282
  }
1282
1283
  if (Array.isArray(current)) {
1283
- this.resolveArrayReferences(current, rootConfig, path13);
1284
+ this.resolveArrayReferences(current, rootConfig, path15);
1284
1285
  return;
1285
1286
  }
1286
1287
  if ("$ref" in current && typeof current.$ref === "string") {
1287
- this.resolveObjectReference(current, rootConfig, path13);
1288
+ this.resolveObjectReference(current, rootConfig, path15);
1288
1289
  return;
1289
1290
  }
1290
- this.resolveObjectProperties(current, rootConfig, path13);
1291
+ this.resolveObjectProperties(current, rootConfig, path15);
1291
1292
  }
1292
1293
  /**
1293
1294
  * Resolve $ref items in arrays and flatten if they point to arrays
1294
1295
  * Example: [{ $ref: "context_definitions.repos" }] where repos is [item1, item2]
1295
1296
  * becomes [item1, item2]
1296
1297
  */
1297
- resolveArrayReferences(arr, rootConfig, path13) {
1298
+ resolveArrayReferences(arr, rootConfig, path15) {
1298
1299
  const result = arr.flatMap((item, i) => {
1299
- const contextPath = `${path13}[${i}]`;
1300
+ const contextPath = `${path15}[${i}]`;
1300
1301
  if (!this.isRefObject(item) || item.$ref.startsWith("#")) {
1301
1302
  this.resolveReferencesRecursive(item, rootConfig, contextPath);
1302
1303
  return [item];
@@ -1314,9 +1315,9 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1314
1315
  * Resolve object reference: { $ref: "path" }
1315
1316
  * Replaces object with resolved data (in-place mutation)
1316
1317
  */
1317
- resolveObjectReference(current, rootConfig, path13) {
1318
+ resolveObjectReference(current, rootConfig, path15) {
1318
1319
  const refPath = current.$ref;
1319
- const contextPath = path13 || "root";
1320
+ const contextPath = path15 || "root";
1320
1321
  if (refPath.startsWith("#")) {
1321
1322
  return;
1322
1323
  }
@@ -1326,24 +1327,24 @@ Import chain: ${[...visitedFiles].join(" \u2192 ")} \u2192 ${normalizedPath}`
1326
1327
  delete current[key];
1327
1328
  }
1328
1329
  Object.assign(current, filteredResolved);
1329
- this.resolveReferencesRecursive(current, rootConfig, path13);
1330
+ this.resolveReferencesRecursive(current, rootConfig, path15);
1330
1331
  }
1331
1332
  /**
1332
1333
  * Resolve object properties recursively
1333
1334
  * Handles both nested objects and "$ref:path" string references
1334
1335
  */
1335
- resolveObjectProperties(current, rootConfig, path13) {
1336
+ resolveObjectProperties(current, rootConfig, path15) {
1336
1337
  for (const [key, value] of Object.entries(current)) {
1337
1338
  if (typeof value === "string" && value.startsWith("$ref:")) {
1338
1339
  const refPath = value.slice(5);
1339
1340
  if (refPath.startsWith("#")) {
1340
1341
  continue;
1341
1342
  }
1342
- const contextPath = path13 ? `${path13}.${key}` : key;
1343
+ const contextPath = path15 ? `${path15}.${key}` : key;
1343
1344
  const resolved = this.resolveReference(rootConfig, refPath, contextPath);
1344
1345
  current[key] = resolved;
1345
1346
  } else {
1346
- this.resolveReferencesRecursive(value, rootConfig, path13 ? `${path13}.${key}` : key);
1347
+ this.resolveReferencesRecursive(value, rootConfig, path15 ? `${path15}.${key}` : key);
1347
1348
  }
1348
1349
  }
1349
1350
  }
@@ -1499,8 +1500,8 @@ async function withRetry(fn, operation, maxAttempts = RATE_LIMITING.RETRY_ATTEMP
1499
1500
  }
1500
1501
  const delayMs = RATE_LIMITING.RETRY_DELAY_MS * 2 ** (attempt - 1);
1501
1502
  logger.warn(` \u26A0\uFE0F Retry ${attempt}/${maxAttempts} for ${operation} after ${delayMs}ms...`);
1502
- await new Promise((resolve4) => {
1503
- const timerId = setTimeout(resolve4, delayMs);
1503
+ await new Promise((resolve6) => {
1504
+ const timerId = setTimeout(resolve6, delayMs);
1504
1505
  timerId.unref();
1505
1506
  });
1506
1507
  }
@@ -2853,13 +2854,13 @@ async function destroyResources(options) {
2853
2854
  if (force) {
2854
2855
  logger.info("\u{1F680} --force flag detected, skipping confirmation\n");
2855
2856
  } else {
2856
- const readline = await import('readline');
2857
- const rl = readline.createInterface({
2857
+ const readline2 = await import('readline');
2858
+ const rl = readline2.createInterface({
2858
2859
  input: process.stdin,
2859
2860
  output: process.stdout
2860
2861
  });
2861
- const answer = await new Promise((resolve4) => {
2862
- rl.question('Type "destroy" to confirm deletion: ', resolve4);
2862
+ const answer = await new Promise((resolve6) => {
2863
+ rl.question('Type "destroy" to confirm deletion: ', resolve6);
2863
2864
  });
2864
2865
  rl.close();
2865
2866
  if (answer.trim().toLowerCase() !== "destroy") {
@@ -2904,6 +2905,222 @@ async function main3(options) {
2904
2905
  process.exit(1);
2905
2906
  }
2906
2907
  }
2908
+ async function promptUser(question, defaultValue) {
2909
+ const rl = readline.createInterface({
2910
+ input: process.stdin,
2911
+ output: process.stdout
2912
+ });
2913
+ const displayQuestion = defaultValue ? `${question} (${defaultValue}): ` : `${question}: `;
2914
+ const answer = await new Promise((resolve6) => {
2915
+ rl.question(displayQuestion, resolve6);
2916
+ });
2917
+ rl.close();
2918
+ const trimmedAnswer = answer.trim();
2919
+ return trimmedAnswer || defaultValue || "";
2920
+ }
2921
+ async function confirmOverwrite(filename) {
2922
+ const answer = await promptUser(`File '${filename}' already exists. Overwrite? (y/n)`);
2923
+ return answer.toLowerCase() === "y";
2924
+ }
2925
+
2926
+ // src/lib/initHelpers.ts
2927
+ function fileExists(filePath) {
2928
+ return fs2.existsSync(filePath);
2929
+ }
2930
+ function generateEnvTemplate() {
2931
+ return `# Codemie API Configuration
2932
+ # Replace these placeholder values with your actual Codemie instance details
2933
+ # DO NOT commit this file to Git!
2934
+
2935
+ # Codemie API endpoint URL
2936
+ CODEMIE_API_URL=https://your-instance.codemie.ai/api
2937
+
2938
+ # Keycloak authentication server URL
2939
+ CODEMIE_AUTH_URL=https://your-keycloak.auth.com
2940
+
2941
+ # Keycloak realm name
2942
+ CODEMIE_REALM=codemie
2943
+
2944
+ # Service account client ID
2945
+ CODEMIE_CLIENT_ID=your-client-id
2946
+
2947
+ # Service account client secret (keep this secure!)
2948
+ CODEMIE_CLIENT_SECRET=your-client-secret
2949
+ `;
2950
+ }
2951
+ function generateMinimalCodemieYaml(projectName, projectDescription) {
2952
+ return `# Codemie Infrastructure as Code Configuration
2953
+ # For detailed examples and documentation, see:
2954
+ # - examples/assistant-examples.yaml
2955
+ # - examples/datasource-examples.yaml
2956
+ # - examples/workflow-examples.yaml
2957
+ # - examples/toolkit-examples.yaml
2958
+ # - examples/modular-config-example/
2959
+
2960
+ version: "1"
2961
+
2962
+ # Project identification
2963
+ project:
2964
+ name: ${projectName}
2965
+ description: ${projectDescription}
2966
+
2967
+ # Environment configuration (uses variables from .env file)
2968
+ environment:
2969
+ codemie_api_url: \${CODEMIE_API_URL}
2970
+ auth_server_url: \${CODEMIE_AUTH_URL}
2971
+ auth_realm_name: \${CODEMIE_REALM}
2972
+ client_id: \${CODEMIE_CLIENT_ID}
2973
+ client_secret: \${CODEMIE_CLIENT_SECRET}
2974
+
2975
+ # Imported resources from existing Codemie platform
2976
+ # Reference existing assistants, datasources, and integrations to use in your IaC configurations
2977
+ imported:
2978
+ assistants: []
2979
+ datasources: []
2980
+ integrations: []
2981
+
2982
+ # Resources to be managed by IaC
2983
+ # Add your assistants, datasources, and workflows here
2984
+ resources:
2985
+ assistants: []
2986
+ datasources: []
2987
+ workflows: []
2988
+ `;
2989
+ }
2990
+ async function promptAppConfig() {
2991
+ const rootDir = await promptUser("Root directory", defaultConfig.rootDir);
2992
+ const codemieConfig = await promptUser("Codemie config file path", defaultConfig.codemieConfig);
2993
+ const codemieState = await promptUser("Codemie state file path", defaultConfig.codemieState);
2994
+ const backupsDirectory = await promptUser("Backups directory", defaultConfig.backupsDirectory);
2995
+ return {
2996
+ rootDir,
2997
+ codemieConfig,
2998
+ codemieState,
2999
+ backupsDirectory
3000
+ };
3001
+ }
3002
+ function compareConfigWithDefaults(userConfig) {
3003
+ return userConfig.rootDir !== defaultConfig.rootDir || userConfig.codemieConfig !== defaultConfig.codemieConfig || userConfig.codemieState !== defaultConfig.codemieState || userConfig.backupsDirectory !== defaultConfig.backupsDirectory;
3004
+ }
3005
+ async function saveConfigFile(config, defaultFilename = "config.json") {
3006
+ const configFilename = await promptUser(`Enter config file path`, defaultFilename);
3007
+ const configPath = path6.resolve(process.cwd(), configFilename);
3008
+ if (fileExists(configPath)) {
3009
+ const shouldOverwrite = await confirmOverwrite(configFilename);
3010
+ if (!shouldOverwrite) {
3011
+ throw new Error(`Cancelled: Configuration file not saved.`);
3012
+ }
3013
+ }
3014
+ fs2.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
3015
+ return configFilename;
3016
+ }
3017
+ function suggestConfigUsage(configFileName) {
3018
+ logger.info("\n\u{1F4CB} To use this configuration with other commands, add the `-c` flag:\n");
3019
+ logger.info(` codemie deploy -c ${configFileName}`);
3020
+ logger.info(` codemie validate -c ${configFileName}`);
3021
+ logger.info(` codemie preview -c ${configFileName}`);
3022
+ logger.info(` codemie backup -c ${configFileName}`);
3023
+ logger.info(` codemie destroy -c ${configFileName}
3024
+ `);
3025
+ }
3026
+
3027
+ // src/init.ts
3028
+ async function initProject(options) {
3029
+ logger.info("\u{1F680} Initialize Codemie IaC Project\n\n");
3030
+ try {
3031
+ const { appConfig } = options;
3032
+ const envPath = path6.resolve(appConfig.rootDir, ".env");
3033
+ const configPath = path6.resolve(appConfig.rootDir, appConfig.codemieConfig);
3034
+ logger.info("This wizard will create the following files:");
3035
+ logger.info(` \u{1F4C4} .env - Environment variables with API credentials`);
3036
+ logger.info(` \u{1F4C4} ${appConfig.codemieConfig} - Codemie IaC configuration
3037
+
3038
+ `);
3039
+ logger.info("\u{1F4DD} Project Information\n");
3040
+ const projectName = await promptUser("Project name", "my-codemie-project");
3041
+ const projectDescription = await promptUser(
3042
+ "Project description",
3043
+ "AI assistants and workflows managed with Codemie IaC"
3044
+ );
3045
+ logger.info("\n\u{1F4CB} Application Configuration\n");
3046
+ logger.info("Configure where Codemie IaC will store files and state:\n");
3047
+ const userAppConfig = await promptAppConfig();
3048
+ let configFileName;
3049
+ if (compareConfigWithDefaults(userAppConfig)) {
3050
+ try {
3051
+ configFileName = await saveConfigFile(userAppConfig);
3052
+ logger.info(`\u2713 Configuration saved to ${configFileName}
3053
+ `);
3054
+ } catch (error) {
3055
+ logger.warn(`
3056
+ \u26A0\uFE0F ${error instanceof Error ? error.message : String(error)}`);
3057
+ logger.warn("Continuing with project initialization...\n");
3058
+ }
3059
+ } else {
3060
+ logger.info("\u2713 Using default configuration\n");
3061
+ }
3062
+ let shouldCreateEnv = true;
3063
+ if (fileExists(envPath)) {
3064
+ logger.warn(`\u26A0\uFE0F File '.env' already exists`);
3065
+ shouldCreateEnv = await confirmOverwrite(".env");
3066
+ if (!shouldCreateEnv) {
3067
+ logger.info(" Keeping existing .env file\n");
3068
+ }
3069
+ }
3070
+ let shouldCreateConfig = true;
3071
+ if (fileExists(configPath)) {
3072
+ logger.warn(`\u26A0\uFE0F File '${appConfig.codemieConfig}' already exists`);
3073
+ shouldCreateConfig = await confirmOverwrite(appConfig.codemieConfig);
3074
+ if (!shouldCreateConfig) {
3075
+ logger.info(` Keeping existing ${appConfig.codemieConfig} file
3076
+ `);
3077
+ }
3078
+ }
3079
+ if (!shouldCreateEnv && !shouldCreateConfig) {
3080
+ logger.info("\u2713 No files created (all existing files kept)\n");
3081
+ return;
3082
+ }
3083
+ logger.info("\n\u{1F4C1} Creating configuration files...\n");
3084
+ if (shouldCreateEnv) {
3085
+ const envContent = generateEnvTemplate();
3086
+ fs2.writeFileSync(envPath, envContent, "utf-8");
3087
+ logger.info(`\u2713 Created .env`);
3088
+ logger.warn(` \u26A0\uFE0F Remember to fill in your actual credentials!`);
3089
+ logger.warn(` \u26A0\uFE0F DO NOT commit .env to Git!
3090
+ `);
3091
+ }
3092
+ if (shouldCreateConfig) {
3093
+ const configContent = generateMinimalCodemieYaml(projectName, projectDescription);
3094
+ fs2.writeFileSync(configPath, configContent, "utf-8");
3095
+ logger.info(`\u2713 Created ${appConfig.codemieConfig}`);
3096
+ logger.info(` See examples/ directory for detailed configuration examples
3097
+
3098
+ `);
3099
+ }
3100
+ logger.info("\u{1F389} Initialization complete!\n");
3101
+ logger.info("Next steps:");
3102
+ logger.info(" 1. Edit .env and add your Codemie API credentials");
3103
+ logger.info(` 2. Edit ${appConfig.codemieConfig} and add your resources`);
3104
+ logger.info(' 3. Run "codemie validate" to check your configuration');
3105
+ logger.info(' 4. Run "codemie deploy" to deploy your resources\n');
3106
+ if (configFileName) {
3107
+ suggestConfigUsage(configFileName);
3108
+ }
3109
+ logger.info("\u{1F4DA} For examples and documentation, see:");
3110
+ logger.info(" \u2022 examples/assistant-examples.yaml");
3111
+ logger.info(" \u2022 examples/datasource-examples.yaml");
3112
+ logger.info(" \u2022 examples/workflow-examples.yaml");
3113
+ logger.info(" \u2022 examples/modular-config-example/\n");
3114
+ } catch (error) {
3115
+ logger.error(`
3116
+ \u274C Initialization failed: ${error instanceof Error ? error.message : String(error)}
3117
+ `);
3118
+ process.exit(1);
3119
+ }
3120
+ }
3121
+ async function main4(options) {
3122
+ await initProject(options);
3123
+ }
2907
3124
  async function previewResource(resource, resourceType, getState, checkExists, calculateChecksums) {
2908
3125
  const existingState = getState(resource.name);
2909
3126
  if (existingState) {
@@ -3094,7 +3311,7 @@ async function previewChanges(appConfig, existingClient) {
3094
3311
  }
3095
3312
  };
3096
3313
  }
3097
- async function main4(options) {
3314
+ async function main5(options) {
3098
3315
  logger.info("\u{1F4CB} Generating deployment preview...\n");
3099
3316
  try {
3100
3317
  const { appConfig } = options;
@@ -3202,10 +3419,10 @@ var DependencyValidator = class {
3202
3419
  const errors = [];
3203
3420
  const visited = /* @__PURE__ */ new Set();
3204
3421
  const recursionStack = /* @__PURE__ */ new Set();
3205
- const dfs = (name, path13) => {
3422
+ const dfs = (name, path15) => {
3206
3423
  if (recursionStack.has(name)) {
3207
- const cycleStart = path13.indexOf(name);
3208
- const cycle = [...path13.slice(cycleStart), name];
3424
+ const cycleStart = path15.indexOf(name);
3425
+ const cycle = [...path15.slice(cycleStart), name];
3209
3426
  errors.push(`Cyclic dependency detected: ${cycle.join(" \u2192 ")}`);
3210
3427
  return true;
3211
3428
  }
@@ -3214,14 +3431,14 @@ var DependencyValidator = class {
3214
3431
  }
3215
3432
  visited.add(name);
3216
3433
  recursionStack.add(name);
3217
- path13.push(name);
3434
+ path15.push(name);
3218
3435
  const assistant = assistantMap.get(name);
3219
3436
  const subAssistantRefs = assistant?.sub_assistants || [];
3220
3437
  for (const subRef of subAssistantRefs) {
3221
3438
  if (!assistantMap.has(subRef)) {
3222
3439
  continue;
3223
3440
  }
3224
- if (dfs(subRef, [...path13])) {
3441
+ if (dfs(subRef, [...path15])) {
3225
3442
  return true;
3226
3443
  }
3227
3444
  }
@@ -3409,7 +3626,7 @@ async function validateConfig(options) {
3409
3626
  }
3410
3627
  return { success: errors.length === 0, errors };
3411
3628
  }
3412
- async function main5(options) {
3629
+ async function main6(options) {
3413
3630
  const { checkApi = false, appConfig } = options;
3414
3631
  logger.info("\u{1F50D} Validating configuration...");
3415
3632
  if (checkApi) {
@@ -3454,6 +3671,10 @@ async function main5(options) {
3454
3671
 
3455
3672
  // src/cli/index.ts
3456
3673
  program.name("codemie").description("Infrastructure as Code for Codemie platform").version(package_default.version);
3674
+ program.command("init").description("Initialize a new Codemie IaC project").action(() => {
3675
+ const appConfig = loadAppConfig();
3676
+ void main4({ appConfig });
3677
+ });
3457
3678
  program.command("deploy").description("Deploy resources to Codemie platform").option("-p, --prune", "Delete orphaned resources").option("-c, --config <path>", "Configuration file").action((options) => {
3458
3679
  const configPath = options.config ? path6__default.resolve(options.config) : void 0;
3459
3680
  const appConfig = loadAppConfig(configPath);
@@ -3472,11 +3693,11 @@ program.command("destroy").description("Destroy all IaC-managed resources").opti
3472
3693
  program.command("preview").description("Preview changes without applying them").option("-c, --config <path>", "Configuration file").action((options) => {
3473
3694
  const appConfigPath = options.config ? path6__default.resolve(options.config) : void 0;
3474
3695
  const appConfig = loadAppConfig(appConfigPath);
3475
- void main4({ appConfig });
3696
+ void main5({ appConfig });
3476
3697
  });
3477
3698
  program.command("validate").description("Validate configuration").option("-a, --check-api", "Check for API conflicts").option("-c, --config <path>", "Configuration file").action((options) => {
3478
3699
  const configPath = options.config ? path6__default.resolve(options.config) : void 0;
3479
3700
  const appConfig = loadAppConfig(configPath);
3480
- void main5({ appConfig, checkApi: options.checkApi });
3701
+ void main6({ appConfig, checkApi: options.checkApi });
3481
3702
  });
3482
3703
  program.parse();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codemieai/cdk",
3
- "version": "0.1.288",
3
+ "version": "0.1.290",
4
4
  "type": "module",
5
5
  "description": "Infrastructure as Code solution for managing Codemie AI assistants, datasources, and workflows through declarative YAML configuration",
6
6
  "main": "./dist/index.js",
@@ -32,7 +32,7 @@
32
32
  "format:check": "biome format ./src",
33
33
  "lint": "npm-run-all types:check biome:check format:check",
34
34
  "lint:check": "npm-run-all types:check biome:check",
35
- "lint:fix": "npm-run-all types:check eslint format",
35
+ "lint:fix": "npm-run-all types:check biome format",
36
36
  "test": "jest --silent --passWithNoTests",
37
37
  "test:ci": "jest --silent --coverage --ci",
38
38
  "test:coverage": "jest --silent --coverage",