@kuckit/cli 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -636,7 +636,7 @@ async function dbStudio(options) {
636
636
 
637
637
  //#endregion
638
638
  //#region src/lib/credentials.ts
639
- const DEFAULT_SERVER_URL = "https://api.kuckit.dev";
639
+ const DEFAULT_SERVER_URL = "https://dev-app-nyh7i73bea-uc.a.run.app/";
640
640
  const CONFIG_DIR = join(homedir(), ".kuckit");
641
641
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
642
642
  function loadConfig$7() {
@@ -1375,6 +1375,20 @@ async function infraDeploy(options) {
1375
1375
  const outputs = result.outputs;
1376
1376
  if (outputs.serviceUrl) console.log(`\nService URL: ${outputs.serviceUrl}`);
1377
1377
  if (outputs.migrationJobName) console.log(`Migration Job: ${outputs.migrationJobName}`);
1378
+ if (outputs.customDomain) {
1379
+ console.log(`\nCustom Domain: ${outputs.customDomain}`);
1380
+ console.log(`Status: ${outputs.customDomainStatus ?? "UNKNOWN"}`);
1381
+ const records = outputs.customDomainRecords;
1382
+ if (records && records.length > 0) {
1383
+ console.log("\nRequired DNS Records:");
1384
+ for (const record of records) console.log(` ${record.type} ${record.name} → ${record.rrdata}`);
1385
+ console.log("\nNote: If using Cloudflare:");
1386
+ console.log(" 1. Set DNS records to DNS-only (gray cloud) until status is READY");
1387
+ console.log(" 2. Certificate provisioning can take 15-60 minutes");
1388
+ console.log(" 3. Once READY, you can enable Cloudflare proxy (orange cloud)");
1389
+ console.log(" 4. Ensure Cloudflare SSL mode is set to \"Full (strict)\"");
1390
+ }
1391
+ }
1378
1392
  }
1379
1393
  console.log("\nUseful commands:");
1380
1394
  console.log(` View logs: kuckit infra logs --env ${env}`);
@@ -1810,7 +1824,7 @@ function runGcloud(args, options = {}) {
1810
1824
  /**
1811
1825
  * Get the path to the packages/infra directory
1812
1826
  */
1813
- function getInfraDir(projectRoot) {
1827
+ function getInfraDir$1(projectRoot) {
1814
1828
  return join$1(projectRoot, "packages", "infra");
1815
1829
  }
1816
1830
  /**
@@ -2110,7 +2124,7 @@ async function infraDestroy(options) {
2110
2124
  console.error(`Error: Invalid environment '${env}'. Must be 'dev' or 'prod'.`);
2111
2125
  process.exit(1);
2112
2126
  }
2113
- const infraDir = getInfraDir(projectRoot);
2127
+ const infraDir = getInfraDir$1(projectRoot);
2114
2128
  if (!await fileExists$6(infraDir)) {
2115
2129
  console.error("Error: packages/infra not found.");
2116
2130
  process.exit(1);
@@ -2263,7 +2277,7 @@ async function infraRepair(options) {
2263
2277
  console.error(`Error: Invalid environment '${env}'. Must be 'dev' or 'prod'.`);
2264
2278
  process.exit(1);
2265
2279
  }
2266
- const infraDir = getInfraDir(projectRoot);
2280
+ const infraDir = getInfraDir$1(projectRoot);
2267
2281
  if (!await fileExists$5(infraDir)) {
2268
2282
  console.error("Error: packages/infra not found.");
2269
2283
  process.exit(1);
@@ -2370,6 +2384,12 @@ async function getJobContext(options) {
2370
2384
  console.error("Run: kuckit infra init");
2371
2385
  process.exit(1);
2372
2386
  }
2387
+ if (!isGcpConfig(config)) {
2388
+ console.error("Error: Database commands only support GCP provider.");
2389
+ console.error(`Current provider: ${config.provider}`);
2390
+ process.exit(1);
2391
+ }
2392
+ const gcpProject = config.providerConfig.gcpProject;
2373
2393
  const env = options.env ?? config.env;
2374
2394
  if (env !== "dev" && env !== "prod") {
2375
2395
  console.error(`Error: Invalid environment '${env}'. Must be 'dev' or 'prod'.`);
@@ -2384,6 +2404,7 @@ async function getJobContext(options) {
2384
2404
  }
2385
2405
  return {
2386
2406
  config,
2407
+ gcpProject,
2387
2408
  projectRoot,
2388
2409
  env,
2389
2410
  jobName
@@ -2460,18 +2481,18 @@ async function infraDbPush(options) {
2460
2481
  console.log("Running database schema push via Cloud Run Job...\n");
2461
2482
  const ctx = await getJobContext(options);
2462
2483
  console.log("Configuration:");
2463
- console.log(` Project: ${ctx.config.gcpProject}`);
2484
+ console.log(` Project: ${ctx.gcpProject}`);
2464
2485
  console.log(` Environment: ${ctx.env}`);
2465
2486
  console.log(` Job: ${ctx.jobName}`);
2466
2487
  console.log("");
2467
2488
  const commandOverride = [`export NODE_PATH=/app/node_modules && cd /app && node /app/node_modules/drizzle-kit/bin.cjs push --config=packages/db/drizzle.config.ts${options.force ? " --force" : ""}`];
2468
- if (!await executeCloudRunJob(ctx.config.gcpProject, ctx.config.region, ctx.jobName, commandOverride)) {
2489
+ if (!await executeCloudRunJob(ctx.gcpProject, ctx.config.region, ctx.jobName, commandOverride)) {
2469
2490
  console.error("\nError: Schema push failed.");
2470
2491
  console.error("Check the Cloud Run Job logs for details:");
2471
2492
  console.error(` gcloud run jobs executions list --job ${ctx.jobName} --region ${ctx.config.region}`);
2472
2493
  process.exit(1);
2473
2494
  }
2474
- const execution = await getLatestJobExecution(ctx.config.gcpProject, ctx.config.region, ctx.jobName);
2495
+ const execution = await getLatestJobExecution(ctx.gcpProject, ctx.config.region, ctx.jobName);
2475
2496
  if (execution?.logs) {
2476
2497
  console.log("\nJob output:");
2477
2498
  console.log(execution.logs);
@@ -2491,17 +2512,17 @@ async function infraDbMigrate(options) {
2491
2512
  }
2492
2513
  const ctx = await getJobContext(options);
2493
2514
  console.log("Configuration:");
2494
- console.log(` Project: ${ctx.config.gcpProject}`);
2515
+ console.log(` Project: ${ctx.gcpProject}`);
2495
2516
  console.log(` Environment: ${ctx.env}`);
2496
2517
  console.log(` Job: ${ctx.jobName}`);
2497
2518
  console.log("");
2498
- if (!await executeCloudRunJob(ctx.config.gcpProject, ctx.config.region, ctx.jobName)) {
2519
+ if (!await executeCloudRunJob(ctx.gcpProject, ctx.config.region, ctx.jobName)) {
2499
2520
  console.error("\nError: Migration failed.");
2500
2521
  console.error("Check the Cloud Run Job logs for details:");
2501
2522
  console.error(` gcloud run jobs executions list --job ${ctx.jobName} --region ${ctx.config.region}`);
2502
2523
  process.exit(1);
2503
2524
  }
2504
- const execution = await getLatestJobExecution(ctx.config.gcpProject, ctx.config.region, ctx.jobName);
2525
+ const execution = await getLatestJobExecution(ctx.gcpProject, ctx.config.region, ctx.jobName);
2505
2526
  if (execution?.logs) {
2506
2527
  console.log("\nJob output:");
2507
2528
  console.log(execution.logs);
@@ -2823,9 +2844,8 @@ async function infraStatus(options) {
2823
2844
  console.error("Run: kuckit infra init");
2824
2845
  process.exit(1);
2825
2846
  }
2826
- const env = options.env ?? config.env ?? "dev";
2827
- const stackName = `${config.gcpProject}-${env}`;
2828
- const infraDir = getInfraDir(projectRoot);
2847
+ const stackName = config.stackName;
2848
+ const infraDir = getInfraDir$1(projectRoot);
2829
2849
  if (!await fileExists$1(infraDir)) {
2830
2850
  console.error("Error: packages/infra not found.");
2831
2851
  process.exit(1);
@@ -2935,8 +2955,8 @@ async function infraOutputs(options) {
2935
2955
  process.exit(1);
2936
2956
  }
2937
2957
  const env = options.env ?? config.env ?? "dev";
2938
- const stackName = `${config.gcpProject}-${env}`;
2939
- const infraDir = getInfraDir(projectRoot);
2958
+ const stackName = config.stackName;
2959
+ const infraDir = getInfraDir$1(projectRoot);
2940
2960
  if (!await fileExists(infraDir)) {
2941
2961
  console.error("Error: packages/infra not found.");
2942
2962
  process.exit(1);
@@ -2982,6 +3002,210 @@ async function infraOutputs(options) {
2982
3002
  console.log("");
2983
3003
  }
2984
3004
 
3005
+ //#endregion
3006
+ //#region src/commands/infra/config.ts
3007
+ /**
3008
+ * Known configuration keys with their descriptions
3009
+ */
3010
+ const KNOWN_CONFIG_KEYS = {
3011
+ appUrl: {
3012
+ description: "Application URL (used for CORS and redirects)",
3013
+ example: "https://app.example.com"
3014
+ },
3015
+ region: {
3016
+ description: "Deployment region",
3017
+ example: "us-central1"
3018
+ },
3019
+ env: {
3020
+ description: "Environment (dev, staging, prod)",
3021
+ example: "prod"
3022
+ }
3023
+ };
3024
+ function getProjectRoot() {
3025
+ return process.cwd();
3026
+ }
3027
+ async function ensureConfigExists(projectRoot) {
3028
+ const config = await loadStoredConfig(projectRoot);
3029
+ if (!config) throw new Error("No infrastructure configuration found. Run `kuckit infra init` first.");
3030
+ return config;
3031
+ }
3032
+ function getStackName(config, env) {
3033
+ return `${config.projectName}-${env}`;
3034
+ }
3035
+ async function getInfraDir(projectRoot, config) {
3036
+ if (config.localInfraDir) return config.localInfraDir;
3037
+ return (await loadProviderFromPackage(config.providerPackage ?? getProviderPackage(config.provider), projectRoot)).getInfraDir(projectRoot);
3038
+ }
3039
+ async function selectStack(infraDir, stackName) {
3040
+ return selectOrCreateStack(stackName, { cwd: infraDir });
3041
+ }
3042
+ async function syncToPulumi(projectRoot, config, env, key, value) {
3043
+ const infraDir = await getInfraDir(projectRoot, config);
3044
+ const stackName = getStackName(config, env);
3045
+ const pulumiOptions = { cwd: infraDir };
3046
+ if (!await selectStack(infraDir, stackName)) throw new Error(`Could not select stack '${stackName}'. Run 'kuckit infra init --env ${env}' first.`);
3047
+ await setPulumiConfig(key, value, pulumiOptions);
3048
+ }
3049
+ async function getPulumiConfigValue(projectRoot, config, env, key) {
3050
+ const infraDir = await getInfraDir(projectRoot, config);
3051
+ const stackName = getStackName(config, env);
3052
+ const pulumiOptions = { cwd: infraDir };
3053
+ if (!await selectStack(infraDir, stackName)) return null;
3054
+ try {
3055
+ const result = await runPulumi([
3056
+ "config",
3057
+ "get",
3058
+ key
3059
+ ], pulumiOptions);
3060
+ return result.code === 0 ? result.stdout.trim() : null;
3061
+ } catch {
3062
+ return null;
3063
+ }
3064
+ }
3065
+ async function getAllPulumiConfig(projectRoot, config, env) {
3066
+ const infraDir = await getInfraDir(projectRoot, config);
3067
+ const stackName = getStackName(config, env);
3068
+ const pulumiOptions = { cwd: infraDir };
3069
+ if (!await selectStack(infraDir, stackName)) return {};
3070
+ try {
3071
+ const result = await runPulumi(["config", "--json"], pulumiOptions);
3072
+ if (result.code === 0 && result.stdout) {
3073
+ const parsed = JSON.parse(result.stdout);
3074
+ const configMap = {};
3075
+ for (const [fullKey, data] of Object.entries(parsed)) {
3076
+ const key = fullKey.includes(":") ? fullKey.split(":")[1] : fullKey;
3077
+ if (key) configMap[key] = data.value;
3078
+ }
3079
+ return configMap;
3080
+ }
3081
+ return {};
3082
+ } catch {
3083
+ return {};
3084
+ }
3085
+ }
3086
+ async function unsetPulumiConfig(projectRoot, config, env, key) {
3087
+ const infraDir = await getInfraDir(projectRoot, config);
3088
+ const stackName = getStackName(config, env);
3089
+ const pulumiOptions = { cwd: infraDir };
3090
+ if (!await selectStack(infraDir, stackName)) throw new Error(`Could not select stack '${stackName}'. Run 'kuckit infra init --env ${env}' first.`);
3091
+ await runPulumi([
3092
+ "config",
3093
+ "rm",
3094
+ key
3095
+ ], pulumiOptions);
3096
+ }
3097
+ /**
3098
+ * Set a configuration value
3099
+ */
3100
+ async function infraConfigSet(key, value, options = {}) {
3101
+ const projectRoot = getProjectRoot();
3102
+ const config = await ensureConfigExists(projectRoot);
3103
+ const env = options.env ?? config.env ?? "dev";
3104
+ if (["region", "projectName"].includes(key)) {
3105
+ if (key === "region") config.region = value;
3106
+ else if (key === "projectName") config.projectName = value;
3107
+ await saveStoredConfig(projectRoot, config);
3108
+ console.log(`✓ Set ${key}=${value} in .kuckit/infra.json`);
3109
+ return;
3110
+ }
3111
+ if (key.startsWith("provider.")) {
3112
+ const providerKey = key.replace("provider.", "");
3113
+ if (isGcpConfig(config) && providerKey === "gcpProject") {
3114
+ config.providerConfig.gcpProject = value;
3115
+ await saveStoredConfig(projectRoot, config);
3116
+ console.log(`✓ Set ${key}=${value} in .kuckit/infra.json`);
3117
+ return;
3118
+ }
3119
+ }
3120
+ await syncToPulumi(projectRoot, config, env, key, value);
3121
+ console.log(`✓ Set ${key}=${value} for env '${env}'`);
3122
+ if (key === "appUrl") console.log(`\nNote: Run 'kuckit infra deploy --env ${env}' to apply this change.`);
3123
+ }
3124
+ /**
3125
+ * Get a configuration value
3126
+ */
3127
+ async function infraConfigGet(key, options = {}) {
3128
+ const projectRoot = getProjectRoot();
3129
+ const config = await ensureConfigExists(projectRoot);
3130
+ const env = options.env ?? config.env ?? "dev";
3131
+ const localKeys = {
3132
+ region: (c) => c.region,
3133
+ projectName: (c) => c.projectName,
3134
+ provider: (c) => c.provider,
3135
+ "provider.gcpProject": (c) => isGcpConfig(c) ? c.providerConfig.gcpProject : void 0
3136
+ };
3137
+ if (key in localKeys) {
3138
+ const getter = localKeys[key];
3139
+ if (getter) {
3140
+ const value$1 = getter(config);
3141
+ if (value$1 !== void 0) {
3142
+ console.log(value$1);
3143
+ return;
3144
+ }
3145
+ }
3146
+ }
3147
+ const value = await getPulumiConfigValue(projectRoot, config, env, key);
3148
+ if (value !== null) console.log(value);
3149
+ else {
3150
+ console.error(`Config key '${key}' is not set for env '${env}'.`);
3151
+ process.exit(1);
3152
+ }
3153
+ }
3154
+ /**
3155
+ * List all configuration values
3156
+ */
3157
+ async function infraConfigList(options = {}) {
3158
+ const projectRoot = getProjectRoot();
3159
+ const config = await ensureConfigExists(projectRoot);
3160
+ const env = options.env ?? config.env ?? "dev";
3161
+ const localConfig = {
3162
+ provider: config.provider,
3163
+ region: config.region,
3164
+ projectName: config.projectName
3165
+ };
3166
+ if (isGcpConfig(config)) localConfig["provider.gcpProject"] = config.providerConfig.gcpProject;
3167
+ const pulumiConfig = await getAllPulumiConfig(projectRoot, config, env);
3168
+ const allConfig = {
3169
+ ...localConfig,
3170
+ ...pulumiConfig
3171
+ };
3172
+ if (options.json) {
3173
+ console.log(JSON.stringify({
3174
+ env,
3175
+ config: allConfig
3176
+ }, null, 2));
3177
+ return;
3178
+ }
3179
+ console.log(`\nInfrastructure Configuration (env: ${env})\n`);
3180
+ console.log("Shared (.kuckit/infra.json):");
3181
+ for (const [key, value] of Object.entries(localConfig)) console.log(` ${key}: ${value}`);
3182
+ if (Object.keys(pulumiConfig).length > 0) {
3183
+ console.log(`\nEnvironment '${env}' Config:`);
3184
+ for (const [key, value] of Object.entries(pulumiConfig)) if (!(key in localConfig)) {
3185
+ const desc = KNOWN_CONFIG_KEYS[key]?.description;
3186
+ console.log(` ${key}: ${value}${desc ? ` (${desc})` : ""}`);
3187
+ }
3188
+ } else console.log(`\nEnvironment '${env}' Config: (none set)`);
3189
+ console.log("\nAvailable keys:");
3190
+ for (const [key, info] of Object.entries(KNOWN_CONFIG_KEYS)) if (!(key in allConfig)) console.log(` ${key}: ${info.description} (e.g., ${info.example})`);
3191
+ console.log(`\nTip: Use --env to configure other environments (dev, staging, prod)`);
3192
+ }
3193
+ /**
3194
+ * Unset a configuration value
3195
+ */
3196
+ async function infraConfigUnset(key, options = {}) {
3197
+ const projectRoot = getProjectRoot();
3198
+ const config = await ensureConfigExists(projectRoot);
3199
+ const env = options.env ?? config.env ?? "dev";
3200
+ if ([
3201
+ "provider",
3202
+ "region",
3203
+ "projectName"
3204
+ ].includes(key)) throw new Error(`Cannot unset required key '${key}'.`);
3205
+ await unsetPulumiConfig(projectRoot, config, env, key);
3206
+ console.log(`✓ Unset ${key} for env '${env}'`);
3207
+ }
3208
+
2985
3209
  //#endregion
2986
3210
  //#region src/bin.ts
2987
3211
  program.name("kuckit").description("CLI tools for Kuckit SDK module development").version("0.1.0");
@@ -3069,6 +3293,23 @@ infra.command("outputs").description("Display infrastructure outputs (URLs, conn
3069
3293
  requireAuth();
3070
3294
  await infraOutputs(options);
3071
3295
  });
3296
+ const configCmd = infra.command("config").description("Manage infrastructure configuration");
3297
+ configCmd.command("set <key> <value>").description("Set a configuration value").option("-e, --env <env>", "Environment (dev, prod)", "dev").action(async (key, value, options) => {
3298
+ requireAuth();
3299
+ await infraConfigSet(key, value, options);
3300
+ });
3301
+ configCmd.command("get <key>").description("Get a configuration value").option("-e, --env <env>", "Environment (dev, prod)", "dev").action(async (key, options) => {
3302
+ requireAuth();
3303
+ await infraConfigGet(key, options);
3304
+ });
3305
+ configCmd.command("list").description("List all configuration values").option("-e, --env <env>", "Environment (dev, prod)", "dev").option("--json", "Output as JSON", false).action(async (options) => {
3306
+ requireAuth();
3307
+ await infraConfigList(options);
3308
+ });
3309
+ configCmd.command("unset <key>").description("Remove a configuration value").option("-e, --env <env>", "Environment (dev, prod)", "dev").action(async (key, options) => {
3310
+ requireAuth();
3311
+ await infraConfigUnset(key, options);
3312
+ });
3072
3313
  program.parse();
3073
3314
 
3074
3315
  //#endregion