@kuckit/cli 1.0.4 → 2.0.0

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
@@ -1,13 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import { i as generateModule, n as loadTryLoadKuckitConfig, r as addModule, t as discoverModules } from "./discover-module-B4oRIuSK.js";
2
+ import { a as isLegacyConfig, c as loadTryLoadKuckitConfig, i as isGcpConfig, l as addModule, o as migrateLegacyConfig, s as discoverModules, u as generateModule } from "./provider-DbqTBb6C.js";
3
3
  import { program } from "commander";
4
4
  import { dirname, join } from "node:path";
5
5
  import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, statSync, unlinkSync, writeFileSync } from "node:fs";
6
- import { execSync, spawn } from "child_process";
6
+ import { spawn } from "child_process";
7
7
  import { access, constants as constants$1, mkdir, readFile, unlink, writeFile } from "fs/promises";
8
8
  import { dirname as dirname$1, join as join$1 } from "path";
9
9
  import { homedir } from "node:os";
10
10
  import { confirm, input, select } from "@inquirer/prompts";
11
+ import { accessSync as accessSync$1, constants as constants$2 } from "fs";
11
12
 
12
13
  //#region src/commands/doctor.ts
13
14
  const CONFIG_FILES = [
@@ -99,7 +100,7 @@ function checkModuleExports(packageName, cwd) {
99
100
  ].some((p) => existsSync(p));
100
101
  return result;
101
102
  }
102
- async function loadConfig$9(configPath) {
103
+ async function loadConfig$8(configPath) {
103
104
  try {
104
105
  if (configPath.endsWith(".ts")) {
105
106
  const { createJiti } = await import("jiti");
@@ -139,7 +140,7 @@ async function doctor(options) {
139
140
  });
140
141
  let configModules = [];
141
142
  if (configPath) {
142
- const { success, config, error } = await loadConfig$9(configPath);
143
+ const { success, config, error } = await loadConfig$8(configPath);
143
144
  if (success && config && typeof config === "object") {
144
145
  const cfg = config;
145
146
  if (Array.isArray(cfg.modules)) {
@@ -462,9 +463,9 @@ async function search(keyword, options) {
462
463
 
463
464
  //#endregion
464
465
  //#region src/commands/db.ts
465
- const KUCKIT_DIR$9 = ".kuckit";
466
+ const KUCKIT_DIR$8 = ".kuckit";
466
467
  const TEMP_CONFIG_NAME = "drizzle.config.ts";
467
- async function fileExists$9(path) {
468
+ async function fileExists$11(path) {
468
469
  try {
469
470
  await access(path, constants$1.F_OK);
470
471
  return true;
@@ -472,10 +473,10 @@ async function fileExists$9(path) {
472
473
  return false;
473
474
  }
474
475
  }
475
- async function findProjectRoot$9(cwd) {
476
+ async function findProjectRoot$11(cwd) {
476
477
  let dir = cwd;
477
478
  while (dir !== dirname$1(dir)) {
478
- if (await fileExists$9(join$1(dir, "package.json"))) return dir;
479
+ if (await fileExists$11(join$1(dir, "package.json"))) return dir;
479
480
  dir = dirname$1(dir);
480
481
  }
481
482
  return null;
@@ -488,7 +489,7 @@ async function getDatabaseUrl(cwd, options) {
488
489
  join$1(cwd, "apps", "server", ".env"),
489
490
  join$1(cwd, ".env.local")
490
491
  ];
491
- for (const envPath of envPaths) if (await fileExists$9(envPath)) try {
492
+ for (const envPath of envPaths) if (await fileExists$11(envPath)) try {
492
493
  const match = (await readFile(envPath, "utf-8")).match(/^DATABASE_URL=(.+)$/m);
493
494
  if (match) return match[1].replace(/^["']|["']$/g, "");
494
495
  } catch {}
@@ -504,10 +505,10 @@ async function discoverModuleSchemas(cwd) {
504
505
  let moduleId = null;
505
506
  if (moduleSpec.package) {
506
507
  const workspacePath = join$1(cwd, "packages", moduleSpec.package.replace(/^@[^/]+\//, ""));
507
- if (await fileExists$9(join$1(workspacePath, "package.json"))) packagePath = workspacePath;
508
+ if (await fileExists$11(join$1(workspacePath, "package.json"))) packagePath = workspacePath;
508
509
  else {
509
510
  const nodeModulesPath = join$1(cwd, "node_modules", moduleSpec.package);
510
- if (await fileExists$9(join$1(nodeModulesPath, "package.json"))) packagePath = nodeModulesPath;
511
+ if (await fileExists$11(join$1(nodeModulesPath, "package.json"))) packagePath = nodeModulesPath;
511
512
  }
512
513
  if (packagePath) try {
513
514
  moduleId = JSON.parse(await readFile(join$1(packagePath, "package.json"), "utf-8")).kuckit?.id || moduleSpec.package;
@@ -522,7 +523,7 @@ async function discoverModuleSchemas(cwd) {
522
523
  join$1(packagePath, "src", "schema", "index.ts"),
523
524
  join$1(packagePath, "src", "schema.ts")
524
525
  ];
525
- for (const schemaPath of schemaLocations) if (await fileExists$9(schemaPath)) {
526
+ for (const schemaPath of schemaLocations) if (await fileExists$11(schemaPath)) {
526
527
  schemas.push({
527
528
  moduleId: moduleId || "unknown",
528
529
  schemaPath
@@ -531,14 +532,14 @@ async function discoverModuleSchemas(cwd) {
531
532
  }
532
533
  }
533
534
  const centralSchemaPath = join$1(cwd, "packages", "db", "src", "schema");
534
- if (await fileExists$9(centralSchemaPath)) schemas.push({
535
+ if (await fileExists$11(centralSchemaPath)) schemas.push({
535
536
  moduleId: "core",
536
537
  schemaPath: centralSchemaPath
537
538
  });
538
539
  return schemas;
539
540
  }
540
541
  async function generateTempDrizzleConfig(cwd, databaseUrl, schemas) {
541
- const kuckitDir = join$1(cwd, KUCKIT_DIR$9);
542
+ const kuckitDir = join$1(cwd, KUCKIT_DIR$8);
542
543
  await mkdir(kuckitDir, { recursive: true });
543
544
  const configPath = join$1(kuckitDir, TEMP_CONFIG_NAME);
544
545
  const schemaPaths = schemas.map((s) => s.schemaPath);
@@ -590,7 +591,7 @@ function runDrizzleKit(command, configPath, cwd) {
590
591
  });
591
592
  }
592
593
  async function runDbCommand(command, options) {
593
- const projectRoot = await findProjectRoot$9(process.cwd());
594
+ const projectRoot = await findProjectRoot$11(process.cwd());
594
595
  if (!projectRoot) {
595
596
  console.error("Error: Could not find project root (no package.json found)");
596
597
  process.exit(1);
@@ -635,10 +636,10 @@ async function dbStudio(options) {
635
636
 
636
637
  //#endregion
637
638
  //#region src/lib/credentials.ts
638
- const DEFAULT_SERVER_URL = "https://dev-app-nyh7i73bea-uc.a.run.app/";
639
+ const DEFAULT_SERVER_URL = "https://api.kuckit.dev";
639
640
  const CONFIG_DIR = join(homedir(), ".kuckit");
640
641
  const CONFIG_PATH = join(CONFIG_DIR, "config.json");
641
- function loadConfig$8() {
642
+ function loadConfig$7() {
642
643
  try {
643
644
  if (!existsSync(CONFIG_PATH)) return null;
644
645
  const content = readFileSync(CONFIG_PATH, "utf-8");
@@ -647,7 +648,7 @@ function loadConfig$8() {
647
648
  return null;
648
649
  }
649
650
  }
650
- function saveConfig$2(config) {
651
+ function saveConfig(config) {
651
652
  if (!existsSync(CONFIG_DIR)) mkdirSync(CONFIG_DIR, {
652
653
  recursive: true,
653
654
  mode: 448
@@ -691,7 +692,7 @@ function formatExpiryDate(expiresAt) {
691
692
  });
692
693
  }
693
694
  async function authLogin(options) {
694
- const config = loadConfig$8();
695
+ const config = loadConfig$7();
695
696
  const serverUrl = getServerUrl(config, options.server);
696
697
  const scopes = options.scopes?.split(",").map((s) => s.trim()) ?? ["cli"];
697
698
  console.log("\nAuthenticating with Kuckit...\n");
@@ -756,7 +757,7 @@ async function authLogin(options) {
756
757
  process.exit(1);
757
758
  }
758
759
  const expiresAt = new Date(Date.now() + pollResult.expiresIn * 1e3).toISOString();
759
- saveConfig$2({
760
+ saveConfig({
760
761
  ...config,
761
762
  cliToken: pollResult.token,
762
763
  tokenExpiresAt: expiresAt,
@@ -789,7 +790,7 @@ async function authLogin(options) {
789
790
  process.exit(1);
790
791
  }
791
792
  async function authWhoami(options) {
792
- const config = loadConfig$8();
793
+ const config = loadConfig$7();
793
794
  if (!config?.cliToken) {
794
795
  if (options.json) console.log(JSON.stringify({ authenticated: false }));
795
796
  else console.log("Not logged in. Run 'kuckit auth login' to authenticate.");
@@ -822,7 +823,7 @@ async function authWhoami(options) {
822
823
  }
823
824
  }
824
825
  async function authLogout() {
825
- if (!loadConfig$8()?.cliToken) {
826
+ if (!loadConfig$7()?.cliToken) {
826
827
  console.log("Already logged out.");
827
828
  return;
828
829
  }
@@ -849,7 +850,7 @@ function registerAuthCommands(program$1) {
849
850
  * Exits the process with an error message if not authenticated.
850
851
  */
851
852
  function requireAuth() {
852
- const config = loadConfig$8();
853
+ const config = loadConfig$7();
853
854
  if (!config?.cliToken) {
854
855
  console.error("\nError: Not logged in. Run 'kuckit auth login' first.\n");
855
856
  process.exit(1);
@@ -860,6 +861,732 @@ function requireAuth() {
860
861
  }
861
862
  }
862
863
 
864
+ //#endregion
865
+ //#region src/lib/package-manager.ts
866
+ /**
867
+ * Detect the package manager used in a project
868
+ *
869
+ * @param cwd - Directory to check for lock files
870
+ * @returns The detected package manager, defaults to 'npm' if none found
871
+ */
872
+ function detectPackageManager(cwd) {
873
+ for (const [file, pm] of Object.entries({
874
+ "bun.lock": "bun",
875
+ "bun.lockb": "bun",
876
+ "package-lock.json": "npm",
877
+ "yarn.lock": "yarn",
878
+ "pnpm-lock.yaml": "pnpm"
879
+ })) try {
880
+ accessSync$1(join$1(cwd, file), constants$2.F_OK);
881
+ return pm;
882
+ } catch {}
883
+ return "npm";
884
+ }
885
+ /**
886
+ * Get the install command for a package manager
887
+ *
888
+ * @param pm - Package manager
889
+ * @param packageName - Package to install
890
+ * @param options - Install options
891
+ * @returns The full install command
892
+ */
893
+ function getInstallCommand(pm, packageName, options = {}) {
894
+ const devFlag = options.dev ? " -D" : "";
895
+ switch (pm) {
896
+ case "bun": return `bun add${devFlag} ${packageName}`;
897
+ case "yarn": return `yarn add${options.dev ? " --dev" : ""} ${packageName}`;
898
+ case "pnpm": return `pnpm add${devFlag} ${packageName}`;
899
+ case "npm":
900
+ default: return `npm install${options.dev ? " --save-dev" : ""} ${packageName}`;
901
+ }
902
+ }
903
+
904
+ //#endregion
905
+ //#region src/commands/infra/provider-loader.ts
906
+ const KUCKIT_DIR$7 = ".kuckit";
907
+ const CONFIG_FILE$7 = "infra.json";
908
+ /**
909
+ * Default provider packages by provider ID
910
+ */
911
+ const DEFAULT_PROVIDER_PACKAGES = {
912
+ gcp: "@kuckit/infra-gcp",
913
+ aws: "@kuckit/infra-aws",
914
+ azure: "@kuckit/infra-azure"
915
+ };
916
+ /**
917
+ * Load the stored infrastructure config from .kuckit/infra.json
918
+ */
919
+ async function loadStoredConfig(projectRoot) {
920
+ const configPath = join$1(projectRoot, KUCKIT_DIR$7, CONFIG_FILE$7);
921
+ try {
922
+ await access(configPath, constants$1.F_OK);
923
+ const content = await readFile(configPath, "utf-8");
924
+ const parsed = JSON.parse(content);
925
+ if (isLegacyConfig(parsed)) return migrateLegacyConfig(parsed);
926
+ return parsed;
927
+ } catch {
928
+ return null;
929
+ }
930
+ }
931
+ /**
932
+ * Save infrastructure config to .kuckit/infra.json
933
+ */
934
+ async function saveStoredConfig(projectRoot, config) {
935
+ const { mkdir: mkdir$1, writeFile: writeFile$1 } = await import("fs/promises");
936
+ const kuckitDir = join$1(projectRoot, KUCKIT_DIR$7);
937
+ await mkdir$1(kuckitDir, { recursive: true });
938
+ const configPath = join$1(kuckitDir, CONFIG_FILE$7);
939
+ config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
940
+ await writeFile$1(configPath, JSON.stringify(config, null, 2), "utf-8");
941
+ }
942
+ /**
943
+ * Error thrown when provider package is not installed
944
+ */
945
+ var ProviderNotInstalledError = class extends Error {
946
+ constructor(packageName, installCommand) {
947
+ super(`Provider package '${packageName}' is not installed.`);
948
+ this.packageName = packageName;
949
+ this.installCommand = installCommand;
950
+ this.name = "ProviderNotInstalledError";
951
+ }
952
+ };
953
+ /**
954
+ * Error thrown when provider package is invalid
955
+ */
956
+ var InvalidProviderError = class extends Error {
957
+ constructor(packageName, reason) {
958
+ super(`Package '${packageName}' is not a valid Kuckit infrastructure provider: ${reason}`);
959
+ this.packageName = packageName;
960
+ this.name = "InvalidProviderError";
961
+ }
962
+ };
963
+ /**
964
+ * Load a provider from an npm package
965
+ *
966
+ * @param packageName - npm package name (e.g., '@kuckit/infra-gcp')
967
+ * @param projectRoot - Optional project root for package manager detection
968
+ * @returns The provider instance
969
+ * @throws ProviderNotInstalledError if package not found
970
+ * @throws InvalidProviderError if package doesn't export a valid provider
971
+ */
972
+ async function loadProviderFromPackage(packageName, projectRoot) {
973
+ try {
974
+ const module = await import(packageName);
975
+ if (!module.provider) throw new InvalidProviderError(packageName, "does not export a 'provider' object");
976
+ const provider = module.provider;
977
+ if (!provider.id || !provider.label || !provider.getInfraDir || !provider.init || !provider.deploy) throw new InvalidProviderError(packageName, "missing required methods (id, label, getInfraDir, init, deploy)");
978
+ return provider;
979
+ } catch (error) {
980
+ if (error instanceof ProviderNotInstalledError || error instanceof InvalidProviderError) throw error;
981
+ if (error instanceof Error && (error.message.includes("Cannot find module") || error.message.includes("Cannot find package"))) throw new ProviderNotInstalledError(packageName, getInstallCommand(detectPackageManager(projectRoot ?? process.cwd()), packageName, { dev: true }));
982
+ throw error;
983
+ }
984
+ }
985
+ /**
986
+ * Get the provider package name for a given provider ID
987
+ */
988
+ function getProviderPackage(providerId) {
989
+ return DEFAULT_PROVIDER_PACKAGES[providerId] ?? `@kuckit/infra-${providerId}`;
990
+ }
991
+ /**
992
+ * Check if a provider package is available
993
+ */
994
+ async function isProviderAvailable(providerId) {
995
+ const packageName = getProviderPackage(providerId);
996
+ try {
997
+ await import(packageName);
998
+ return true;
999
+ } catch {
1000
+ return false;
1001
+ }
1002
+ }
1003
+ /**
1004
+ * List available provider packages
1005
+ */
1006
+ async function listAvailableProviders() {
1007
+ return await Promise.all(Object.entries(DEFAULT_PROVIDER_PACKAGES).map(async ([id, pkg]) => ({
1008
+ id,
1009
+ package: pkg,
1010
+ available: await isProviderAvailable(id)
1011
+ })));
1012
+ }
1013
+
1014
+ //#endregion
1015
+ //#region src/commands/infra/init.ts
1016
+ async function fileExists$10(path) {
1017
+ try {
1018
+ await access(path, constants$1.F_OK);
1019
+ return true;
1020
+ } catch {
1021
+ return false;
1022
+ }
1023
+ }
1024
+ async function findProjectRoot$10(cwd) {
1025
+ let dir = cwd;
1026
+ while (dir !== dirname$1(dir)) {
1027
+ if (await fileExists$10(join$1(dir, "package.json"))) return dir;
1028
+ dir = dirname$1(dir);
1029
+ }
1030
+ return null;
1031
+ }
1032
+ /**
1033
+ * Check if legacy packages/infra directory exists
1034
+ */
1035
+ async function hasLegacyInfra(projectRoot) {
1036
+ return fileExists$10(join$1(projectRoot, "packages", "infra", "package.json"));
1037
+ }
1038
+ async function infraInit(options) {
1039
+ console.log("Initializing kuckit infrastructure...\n");
1040
+ const projectRoot = await findProjectRoot$10(process.cwd());
1041
+ if (!projectRoot) {
1042
+ console.error("Error: Could not find project root (no package.json found)");
1043
+ process.exit(1);
1044
+ }
1045
+ if (await hasLegacyInfra(projectRoot)) {
1046
+ console.log("⚠️ Legacy infrastructure detected: packages/infra");
1047
+ console.log("");
1048
+ console.log("Kuckit now uses provider packages (@kuckit/infra-gcp) instead of");
1049
+ console.log("local Pulumi code. Your existing Pulumi state will be preserved.");
1050
+ console.log("");
1051
+ console.log("After migration, you can remove packages/infra.");
1052
+ console.log("See docs/MIGRATION.md for details.");
1053
+ console.log("");
1054
+ }
1055
+ const existingConfig = await loadStoredConfig(projectRoot);
1056
+ let providerId = options.provider ?? existingConfig?.provider ?? "gcp";
1057
+ if (!options.provider && !existingConfig) {
1058
+ const installedProviders = (await listAvailableProviders()).filter((p) => p.available);
1059
+ if (installedProviders.length === 0) {
1060
+ const installCmd = getInstallCommand(detectPackageManager(projectRoot), "@kuckit/infra-gcp", { dev: true });
1061
+ console.error("Error: No infrastructure providers installed.");
1062
+ console.error("");
1063
+ console.error("Kuckit requires a cloud provider package to deploy infrastructure.");
1064
+ console.error("Install a provider to get started:");
1065
+ console.error("");
1066
+ console.error(` ${installCmd}`);
1067
+ console.error("");
1068
+ console.error("Available providers:");
1069
+ console.error(" @kuckit/infra-gcp - Google Cloud Platform (Cloud Run, Cloud SQL, Redis)");
1070
+ console.error(" @kuckit/infra-aws - Amazon Web Services (coming soon)");
1071
+ console.error(" @kuckit/infra-azure - Microsoft Azure (coming soon)");
1072
+ process.exit(1);
1073
+ }
1074
+ providerId = installedProviders[0].id;
1075
+ }
1076
+ const providerPackage = getProviderPackage(providerId);
1077
+ let provider;
1078
+ try {
1079
+ provider = await loadProviderFromPackage(providerPackage, projectRoot);
1080
+ } catch (error) {
1081
+ if (error instanceof ProviderNotInstalledError) {
1082
+ console.error(`Error: ${error.message}`);
1083
+ console.error("");
1084
+ console.error("Install the provider package:");
1085
+ console.error(` ${error.installCommand}`);
1086
+ process.exit(1);
1087
+ }
1088
+ if (error instanceof InvalidProviderError) {
1089
+ console.error(`Error: ${error.message}`);
1090
+ console.error("");
1091
+ console.error("This may be a corrupted or incompatible package version.");
1092
+ console.error("Try reinstalling the provider package.");
1093
+ process.exit(1);
1094
+ }
1095
+ console.error(`Error: ${error instanceof Error ? error.message : "Failed to load provider"}`);
1096
+ process.exit(1);
1097
+ }
1098
+ console.log("Checking prerequisites...");
1099
+ const prereqResult = await provider.checkPrerequisites();
1100
+ if (!prereqResult.ok) {
1101
+ console.error("\nError: Missing required tools:");
1102
+ for (const missing of prereqResult.missing) {
1103
+ console.error(` - ${missing.name}`);
1104
+ console.error(` Install from: ${missing.installUrl}`);
1105
+ }
1106
+ process.exit(1);
1107
+ }
1108
+ if (provider.validateProject) {
1109
+ const validation = await provider.validateProject(projectRoot);
1110
+ if (!validation.valid) {
1111
+ console.error("\nError: Project validation failed:");
1112
+ for (const issue of validation.issues.filter((i) => i.severity === "error")) console.error(` - ${issue.message}`);
1113
+ process.exit(1);
1114
+ }
1115
+ for (const issue of validation.issues.filter((i) => i.severity === "warning")) console.warn(`Warning: ${issue.message}`);
1116
+ }
1117
+ const prompts = await provider.getInitPrompts(existingConfig ?? void 0);
1118
+ const responses = {};
1119
+ if (options.project && providerId === "gcp") responses.gcpProject = options.project;
1120
+ for (const prompt of prompts) {
1121
+ if (responses[prompt.name] !== void 0) continue;
1122
+ if (prompt.type === "input") responses[prompt.name] = await input({
1123
+ message: prompt.message,
1124
+ default: prompt.default,
1125
+ validate: prompt.validate
1126
+ });
1127
+ else if (prompt.type === "select" && prompt.choices) responses[prompt.name] = await select({
1128
+ message: prompt.message,
1129
+ choices: prompt.choices.map((c) => ({
1130
+ value: c.value,
1131
+ name: c.label
1132
+ })),
1133
+ default: prompt.default
1134
+ });
1135
+ else if (prompt.type === "confirm") responses[prompt.name] = await confirm({
1136
+ message: prompt.message,
1137
+ default: prompt.default
1138
+ });
1139
+ }
1140
+ let region = options.region ?? existingConfig?.region ?? "us-central1";
1141
+ if (!options.region && !existingConfig?.region) region = await input({
1142
+ message: "Deployment region:",
1143
+ default: "us-central1"
1144
+ });
1145
+ let env = options.env ?? existingConfig?.env ?? "dev";
1146
+ if (!options.env && !existingConfig?.env) env = await select({
1147
+ message: "Environment:",
1148
+ choices: [
1149
+ {
1150
+ value: "dev",
1151
+ name: "Development"
1152
+ },
1153
+ {
1154
+ value: "staging",
1155
+ name: "Staging"
1156
+ },
1157
+ {
1158
+ value: "prod",
1159
+ name: "Production"
1160
+ }
1161
+ ],
1162
+ default: "dev"
1163
+ });
1164
+ const packageJsonPath = join$1(projectRoot, "package.json");
1165
+ let projectName = "kuckit-app";
1166
+ try {
1167
+ const { readFile: readFile$1 } = await import("fs/promises");
1168
+ projectName = JSON.parse(await readFile$1(packageJsonPath, "utf-8")).name?.replace(/^@[^/]+\//, "") ?? "kuckit-app";
1169
+ } catch {
1170
+ projectName = projectRoot.split("/").pop() ?? "kuckit-app";
1171
+ }
1172
+ const stackName = `${responses.gcpProject ?? ""}-${env}`;
1173
+ console.log("\nConfiguration:");
1174
+ console.log(` Provider: ${provider.label}`);
1175
+ for (const [key, value] of Object.entries(responses)) console.log(` ${key}: ${value}`);
1176
+ console.log(` Region: ${region}`);
1177
+ console.log(` Environment: ${env}`);
1178
+ console.log(` Stack: ${stackName}`);
1179
+ console.log("");
1180
+ if (!options.yes) {
1181
+ if (!await confirm({
1182
+ message: "Proceed with infrastructure initialization?",
1183
+ default: true
1184
+ })) {
1185
+ console.log("Aborted.");
1186
+ process.exit(0);
1187
+ }
1188
+ }
1189
+ const dockerfilePath = join$1(projectRoot, "Dockerfile");
1190
+ if (provider.generateDockerfile && !await fileExists$10(dockerfilePath)) {
1191
+ console.log("\nGenerating Dockerfile...");
1192
+ const dockerfileContent = await provider.generateDockerfile(projectRoot);
1193
+ if (dockerfileContent) {
1194
+ const { writeFile: writeFile$1 } = await import("fs/promises");
1195
+ await writeFile$1(dockerfilePath, dockerfileContent, "utf-8");
1196
+ console.log("Dockerfile created.");
1197
+ }
1198
+ }
1199
+ console.log("\nInitializing infrastructure...");
1200
+ console.log("This may take several minutes.\n");
1201
+ const result = await provider.init({
1202
+ env,
1203
+ region,
1204
+ projectRoot,
1205
+ yes: options.yes,
1206
+ providerOptions: responses
1207
+ });
1208
+ if (!result.success) {
1209
+ console.error(`\nError: ${result.message}`);
1210
+ if (result.error) console.error(` ${result.error.code}: ${result.error.message}`);
1211
+ process.exit(1);
1212
+ }
1213
+ const baseConfig = {
1214
+ env,
1215
+ region,
1216
+ projectName,
1217
+ stackName
1218
+ };
1219
+ await saveStoredConfig(projectRoot, {
1220
+ ...provider.buildConfig(responses, baseConfig),
1221
+ providerPackage,
1222
+ outputs: result.outputs,
1223
+ createdAt: existingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1224
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1225
+ });
1226
+ console.log("\nConfiguration saved to .kuckit/infra.json");
1227
+ console.log("\n" + "=".repeat(60));
1228
+ console.log("Infrastructure initialized successfully!");
1229
+ console.log("=".repeat(60));
1230
+ if (result.outputs) {
1231
+ console.log("\nOutputs:");
1232
+ const outputs = result.outputs;
1233
+ if (outputs.registryUrl) console.log(` Registry: ${outputs.registryUrl}`);
1234
+ if (outputs.databaseConnectionName) console.log(` Database: ${outputs.databaseConnectionName}`);
1235
+ if (outputs.redisHost) console.log(` Redis: ${outputs.redisHost}`);
1236
+ }
1237
+ console.log("\nNext steps:");
1238
+ console.log(" 1. Ensure you have a Dockerfile in your project root");
1239
+ console.log(` 2. Run: kuckit infra deploy --env ${env}`);
1240
+ console.log("");
1241
+ }
1242
+
1243
+ //#endregion
1244
+ //#region src/commands/infra/deploy.ts
1245
+ async function fileExists$9(path) {
1246
+ try {
1247
+ await access(path, constants$1.F_OK);
1248
+ return true;
1249
+ } catch {
1250
+ return false;
1251
+ }
1252
+ }
1253
+ async function findProjectRoot$9(cwd) {
1254
+ let dir = cwd;
1255
+ while (dir !== dirname$1(dir)) {
1256
+ if (await fileExists$9(join$1(dir, "package.json"))) return dir;
1257
+ dir = dirname$1(dir);
1258
+ }
1259
+ return null;
1260
+ }
1261
+ async function infraDeploy(options) {
1262
+ console.log("Deploying kuckit application...\n");
1263
+ const projectRoot = await findProjectRoot$9(process.cwd());
1264
+ if (!projectRoot) {
1265
+ console.error("Error: Could not find project root (no package.json found)");
1266
+ process.exit(1);
1267
+ }
1268
+ const config = await loadStoredConfig(projectRoot);
1269
+ if (!config) {
1270
+ console.error("Error: No infrastructure configuration found.");
1271
+ console.error("Run: kuckit infra init");
1272
+ process.exit(1);
1273
+ }
1274
+ const env = options.env ?? config.env;
1275
+ if (env !== "dev" && env !== "staging" && env !== "prod") {
1276
+ console.error(`Error: Invalid environment '${env}'. Must be 'dev', 'staging', or 'prod'.`);
1277
+ process.exit(1);
1278
+ }
1279
+ const providerPackage = config.providerPackage ?? getProviderPackage(config.provider);
1280
+ let provider;
1281
+ try {
1282
+ provider = await loadProviderFromPackage(providerPackage, projectRoot);
1283
+ } catch (error) {
1284
+ if (error instanceof ProviderNotInstalledError) {
1285
+ console.error(`Error: ${error.message}`);
1286
+ console.error("");
1287
+ console.error("Install the provider package:");
1288
+ console.error(` ${error.installCommand}`);
1289
+ process.exit(1);
1290
+ }
1291
+ if (error instanceof InvalidProviderError) {
1292
+ console.error(`Error: ${error.message}`);
1293
+ console.error("");
1294
+ console.error("This may be a corrupted or incompatible package version.");
1295
+ console.error("Try reinstalling the provider package.");
1296
+ process.exit(1);
1297
+ }
1298
+ console.error(`Error: ${error instanceof Error ? error.message : "Failed to load provider"}`);
1299
+ process.exit(1);
1300
+ }
1301
+ const prereqResult = await provider.checkPrerequisites();
1302
+ if (!prereqResult.ok) {
1303
+ console.error("\nError: Missing required tools:");
1304
+ for (const missing of prereqResult.missing) {
1305
+ console.error(` - ${missing.name}`);
1306
+ console.error(` Install from: ${missing.installUrl}`);
1307
+ }
1308
+ process.exit(1);
1309
+ }
1310
+ const dockerfilePath = join$1(projectRoot, "Dockerfile");
1311
+ if (!options.skipBuild && !options.image && !await fileExists$9(dockerfilePath)) if (provider.generateDockerfile) {
1312
+ console.log("Generating Dockerfile...");
1313
+ const dockerfileContent = await provider.generateDockerfile(projectRoot);
1314
+ if (dockerfileContent) {
1315
+ const { writeFile: writeFile$1 } = await import("fs/promises");
1316
+ await writeFile$1(dockerfilePath, dockerfileContent, "utf-8");
1317
+ console.log("Dockerfile created.\n");
1318
+ } else {
1319
+ console.error("Error: Dockerfile not found in project root.");
1320
+ console.error("Create a Dockerfile or use --skip-build with an existing image.");
1321
+ process.exit(1);
1322
+ }
1323
+ } else {
1324
+ console.error("Error: Dockerfile not found in project root.");
1325
+ console.error("Create a Dockerfile or use --skip-build with an existing image.");
1326
+ process.exit(1);
1327
+ }
1328
+ if (!config.outputs?.registryUrl) {
1329
+ console.error("Error: No registry URL found in configuration.");
1330
+ console.error("Run: kuckit infra init");
1331
+ process.exit(1);
1332
+ }
1333
+ let projectDisplay = config.projectName;
1334
+ if (isGcpConfig(config)) projectDisplay = config.providerConfig.gcpProject;
1335
+ console.log("Configuration:");
1336
+ console.log(` Provider: ${provider.label}`);
1337
+ console.log(` Project: ${projectDisplay}`);
1338
+ console.log(` Region: ${config.region}`);
1339
+ console.log(` Environment: ${env}`);
1340
+ console.log(` Registry: ${config.outputs.registryUrl}`);
1341
+ if (options.preview) console.log(" Mode: Preview (no changes will be applied)");
1342
+ console.log("");
1343
+ if (!options.yes && !options.preview) {
1344
+ if (!await confirm({
1345
+ message: "Proceed with deployment?",
1346
+ default: true
1347
+ })) {
1348
+ console.log("Aborted.");
1349
+ process.exit(0);
1350
+ }
1351
+ }
1352
+ const result = await provider.deploy({
1353
+ env,
1354
+ projectRoot,
1355
+ preview: options.preview,
1356
+ skipBuild: options.skipBuild,
1357
+ image: options.image,
1358
+ yes: options.yes
1359
+ }, config);
1360
+ if (!result.success) {
1361
+ console.error(`\nError: ${result.message}`);
1362
+ if (result.error) console.error(` ${result.error.code}: ${result.error.message}`);
1363
+ process.exit(1);
1364
+ }
1365
+ if (!options.preview && result.outputs) await saveStoredConfig(projectRoot, {
1366
+ ...config,
1367
+ outputs: result.outputs,
1368
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1369
+ });
1370
+ if (!options.preview) {
1371
+ console.log("\n" + "=".repeat(60));
1372
+ console.log("Deployment successful!");
1373
+ console.log("=".repeat(60));
1374
+ if (result.outputs) {
1375
+ const outputs = result.outputs;
1376
+ if (outputs.serviceUrl) console.log(`\nService URL: ${outputs.serviceUrl}`);
1377
+ if (outputs.migrationJobName) console.log(`Migration Job: ${outputs.migrationJobName}`);
1378
+ }
1379
+ console.log("\nUseful commands:");
1380
+ console.log(` View logs: kuckit infra logs --env ${env}`);
1381
+ console.log(` Check status: kuckit infra status --env ${env}`);
1382
+ console.log(` Run migrations: kuckit infra db:migrate --env ${env}`);
1383
+ console.log("");
1384
+ } else {
1385
+ console.log("\nPreview complete. No changes were applied.");
1386
+ console.log("Run without --preview to apply changes.");
1387
+ }
1388
+ }
1389
+
1390
+ //#endregion
1391
+ //#region src/commands/infra/up.ts
1392
+ async function fileExists$8(path) {
1393
+ try {
1394
+ await access(path, constants$1.F_OK);
1395
+ return true;
1396
+ } catch {
1397
+ return false;
1398
+ }
1399
+ }
1400
+ async function findProjectRoot$8(cwd) {
1401
+ let dir = cwd;
1402
+ while (dir !== dirname$1(dir)) {
1403
+ if (await fileExists$8(join$1(dir, "package.json"))) return dir;
1404
+ dir = dirname$1(dir);
1405
+ }
1406
+ return null;
1407
+ }
1408
+ async function infraUp(options) {
1409
+ console.log("🚀 Kuckit Infrastructure Up\n");
1410
+ const projectRoot = await findProjectRoot$8(process.cwd());
1411
+ if (!projectRoot) {
1412
+ console.error("Error: Could not find project root (no package.json found)");
1413
+ process.exit(1);
1414
+ }
1415
+ const existingConfig = await loadStoredConfig(projectRoot);
1416
+ if (!existingConfig) {
1417
+ const providerPackage = getProviderPackage(options.provider ?? "gcp");
1418
+ let providerLabel = "cloud";
1419
+ try {
1420
+ providerLabel = (await loadProviderFromPackage(providerPackage, projectRoot)).label;
1421
+ } catch (error) {
1422
+ if (error instanceof ProviderNotInstalledError) {
1423
+ console.error(`Error: ${error.message}`);
1424
+ console.error("");
1425
+ console.error("Install the provider package first:");
1426
+ console.error(` ${error.installCommand}`);
1427
+ console.error("");
1428
+ console.error("Then run this command again.");
1429
+ process.exit(1);
1430
+ }
1431
+ }
1432
+ console.log("No infrastructure configuration found. Starting initialization...\n");
1433
+ if (!options.yes) {
1434
+ if (!await confirm({
1435
+ message: `This will create ${providerLabel} infrastructure (VPC, Database, Redis, Registry). Continue?`,
1436
+ default: true
1437
+ })) {
1438
+ console.log("Aborted.");
1439
+ process.exit(0);
1440
+ }
1441
+ }
1442
+ await infraInit({
1443
+ provider: options.provider,
1444
+ project: options.project,
1445
+ region: options.region,
1446
+ env: options.env,
1447
+ yes: options.yes
1448
+ });
1449
+ if (!await loadStoredConfig(projectRoot)) {
1450
+ console.error("Error: Initialization completed but no configuration found.");
1451
+ process.exit(1);
1452
+ }
1453
+ console.log("\n" + "─".repeat(60) + "\n");
1454
+ console.log("Infrastructure initialized! Proceeding to deployment...\n");
1455
+ } else {
1456
+ console.log(`Using existing configuration for ${existingConfig.env} environment.`);
1457
+ console.log(`Provider: ${existingConfig.provider}`);
1458
+ console.log(`Region: ${existingConfig.region}\n`);
1459
+ }
1460
+ await infraDeploy({
1461
+ env: options.env ?? existingConfig?.env ?? "dev",
1462
+ preview: options.preview,
1463
+ yes: options.yes
1464
+ });
1465
+ console.log("\n" + "═".repeat(60));
1466
+ console.log("✅ Infrastructure up and running!");
1467
+ console.log("═".repeat(60));
1468
+ console.log("\nUseful commands:");
1469
+ console.log(` kuckit infra status - Check current status`);
1470
+ console.log(` kuckit infra logs - View application logs`);
1471
+ console.log(` kuckit infra outputs - Show service URLs and secrets`);
1472
+ console.log("");
1473
+ }
1474
+
1475
+ //#endregion
1476
+ //#region src/commands/infra/eject.ts
1477
+ async function fileExists$7(path) {
1478
+ try {
1479
+ await access(path, constants$1.F_OK);
1480
+ return true;
1481
+ } catch {
1482
+ return false;
1483
+ }
1484
+ }
1485
+ async function findProjectRoot$7(cwd) {
1486
+ let dir = cwd;
1487
+ while (dir !== dirname$1(dir)) {
1488
+ if (await fileExists$7(join$1(dir, "package.json"))) return dir;
1489
+ dir = dirname$1(dir);
1490
+ }
1491
+ return null;
1492
+ }
1493
+ async function infraEject(options) {
1494
+ console.log("Ejecting infrastructure code...\n");
1495
+ const projectRoot = await findProjectRoot$7(process.cwd());
1496
+ if (!projectRoot) {
1497
+ console.error("Error: Could not find project root (no package.json found)");
1498
+ process.exit(1);
1499
+ }
1500
+ const config = await loadStoredConfig(projectRoot);
1501
+ if (!config) {
1502
+ console.error("Error: No infrastructure configuration found.");
1503
+ console.error("Run: kuckit infra init");
1504
+ process.exit(1);
1505
+ }
1506
+ if (config.localInfraDir) {
1507
+ console.error(`Error: Infrastructure already ejected to ${config.localInfraDir}`);
1508
+ console.error("Remove localInfraDir from .kuckit/infra.json to re-eject.");
1509
+ process.exit(1);
1510
+ }
1511
+ const providerPackage = config.providerPackage ?? getProviderPackage(config.provider);
1512
+ let provider;
1513
+ try {
1514
+ provider = await loadProviderFromPackage(providerPackage, projectRoot);
1515
+ } catch (error) {
1516
+ if (error instanceof ProviderNotInstalledError) {
1517
+ console.error(`Error: ${error.message}`);
1518
+ console.error("");
1519
+ console.error("Install the provider package:");
1520
+ console.error(` ${error.installCommand}`);
1521
+ process.exit(1);
1522
+ }
1523
+ if (error instanceof InvalidProviderError) {
1524
+ console.error(`Error: ${error.message}`);
1525
+ process.exit(1);
1526
+ }
1527
+ console.error(`Error: ${error instanceof Error ? error.message : "Failed to load provider"}`);
1528
+ process.exit(1);
1529
+ }
1530
+ if (!provider.eject) {
1531
+ console.error(`Error: Provider '${provider.id}' does not support ejecting.`);
1532
+ process.exit(1);
1533
+ }
1534
+ const targetDir = options.dir ?? "infra";
1535
+ const absoluteTargetDir = join$1(projectRoot, targetDir);
1536
+ if (await fileExists$7(absoluteTargetDir)) {
1537
+ if (!options.force) {
1538
+ console.error(`Error: Directory '${targetDir}' already exists.`);
1539
+ console.error("Use --force to overwrite, or specify a different directory with --dir.");
1540
+ process.exit(1);
1541
+ }
1542
+ }
1543
+ console.log("Configuration:");
1544
+ console.log(` Provider: ${provider.label}`);
1545
+ console.log(` Target directory: ${targetDir}`);
1546
+ console.log("");
1547
+ if (!options.force) {
1548
+ console.log("This will:");
1549
+ console.log(` 1. Copy provider Pulumi code to ${targetDir}/`);
1550
+ console.log(" 2. Update .kuckit/infra.json to use local infrastructure");
1551
+ console.log(" 3. Future deployments will use your local copy");
1552
+ console.log("");
1553
+ if (!await confirm({
1554
+ message: "Proceed with eject?",
1555
+ default: true
1556
+ })) {
1557
+ console.log("Aborted.");
1558
+ process.exit(0);
1559
+ }
1560
+ }
1561
+ const result = await provider.eject({
1562
+ targetDir: absoluteTargetDir,
1563
+ projectRoot,
1564
+ force: options.force
1565
+ });
1566
+ if (!result.success) {
1567
+ console.error(`\nError: ${result.message}`);
1568
+ if (result.error) console.error(` ${result.error.code}: ${result.error.message}`);
1569
+ process.exit(1);
1570
+ }
1571
+ await saveStoredConfig(projectRoot, {
1572
+ ...config,
1573
+ localInfraDir: targetDir,
1574
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1575
+ });
1576
+ console.log("\n" + "=".repeat(60));
1577
+ console.log("Infrastructure ejected successfully!");
1578
+ console.log("=".repeat(60));
1579
+ console.log(`\nYour infrastructure code is now in: ${targetDir}/`);
1580
+ console.log("");
1581
+ console.log("Next steps:");
1582
+ console.log(` 1. Review and customize the Pulumi code in ${targetDir}/`);
1583
+ console.log(" 2. Install Pulumi dependencies: cd " + targetDir + " && bun install");
1584
+ console.log(" 3. Deploy with: kuckit infra deploy");
1585
+ console.log("");
1586
+ console.log("The CLI will now use your local infrastructure instead of the provider package.");
1587
+ console.log("");
1588
+ }
1589
+
863
1590
  //#endregion
864
1591
  //#region src/commands/infra/runner.ts
865
1592
  /**
@@ -1081,31 +1808,6 @@ function runGcloud(args, options = {}) {
1081
1808
  });
1082
1809
  }
1083
1810
  /**
1084
- * Build and push Docker image using Cloud Build
1085
- * No local Docker daemon required!
1086
- */
1087
- async function buildAndPushImage(options) {
1088
- const tag = options.tag ?? process.env.GIT_SHA ?? "latest";
1089
- const imageUrl = `${options.registryUrl}/kuckit:${tag}`;
1090
- console.log(`Building image: ${imageUrl}`);
1091
- const args = [
1092
- "builds",
1093
- "submit",
1094
- "--tag",
1095
- imageUrl,
1096
- "--project",
1097
- options.project
1098
- ];
1099
- if (options.dockerfile && options.dockerfile !== "Dockerfile") args.push("--config", options.dockerfile);
1100
- return {
1101
- code: (await runGcloud(args, {
1102
- stream: true,
1103
- cwd: options.cwd
1104
- })).code,
1105
- imageUrl
1106
- };
1107
- }
1108
- /**
1109
1811
  * Get the path to the packages/infra directory
1110
1812
  */
1111
1813
  function getInfraDir(projectRoot) {
@@ -1353,360 +2055,6 @@ function formatError(error) {
1353
2055
  return lines.join("\n");
1354
2056
  }
1355
2057
 
1356
- //#endregion
1357
- //#region src/commands/infra/init.ts
1358
- const KUCKIT_DIR$8 = ".kuckit";
1359
- const CONFIG_FILE$8 = "infra.json";
1360
- async function fileExists$8(path) {
1361
- try {
1362
- await access(path, constants$1.F_OK);
1363
- return true;
1364
- } catch {
1365
- return false;
1366
- }
1367
- }
1368
- async function findProjectRoot$8(cwd) {
1369
- let dir = cwd;
1370
- while (dir !== dirname$1(dir)) {
1371
- if (await fileExists$8(join$1(dir, "package.json"))) return dir;
1372
- dir = dirname$1(dir);
1373
- }
1374
- return null;
1375
- }
1376
- async function loadExistingConfig(projectRoot) {
1377
- const configPath = join$1(projectRoot, KUCKIT_DIR$8, CONFIG_FILE$8);
1378
- if (!await fileExists$8(configPath)) return null;
1379
- try {
1380
- const content = await readFile(configPath, "utf-8");
1381
- return JSON.parse(content);
1382
- } catch {
1383
- return null;
1384
- }
1385
- }
1386
- async function saveConfig$1(projectRoot, config) {
1387
- const kuckitDir = join$1(projectRoot, KUCKIT_DIR$8);
1388
- await mkdir(kuckitDir, { recursive: true });
1389
- await writeFile(join$1(kuckitDir, CONFIG_FILE$8), JSON.stringify(config, null, 2), "utf-8");
1390
- }
1391
- async function infraInit(options) {
1392
- console.log("Initializing kuckit infrastructure...\n");
1393
- if (!await checkPulumiInstalled()) {
1394
- console.error("Error: Pulumi CLI is not installed.");
1395
- console.error("Install it from: https://www.pulumi.com/docs/install/");
1396
- process.exit(1);
1397
- }
1398
- if (!await checkGcloudInstalled()) {
1399
- console.error("Error: gcloud CLI is not installed.");
1400
- console.error("Install it from: https://cloud.google.com/sdk/docs/install");
1401
- process.exit(1);
1402
- }
1403
- const projectRoot = await findProjectRoot$8(process.cwd());
1404
- if (!projectRoot) {
1405
- console.error("Error: Could not find project root (no package.json found)");
1406
- process.exit(1);
1407
- }
1408
- const infraDir = getInfraDir(projectRoot);
1409
- if (!await fileExists$8(infraDir)) {
1410
- console.error("Error: packages/infra not found.");
1411
- console.error("Make sure you have the @kuckit/infra package in your project.");
1412
- process.exit(1);
1413
- }
1414
- const existingConfig = await loadExistingConfig(projectRoot);
1415
- const provider = options.provider ?? "gcp";
1416
- if (provider !== "gcp") {
1417
- console.error(`Error: Provider '${provider}' is not supported. Only 'gcp' is currently supported.`);
1418
- process.exit(1);
1419
- }
1420
- let gcpProject = options.project ?? existingConfig?.gcpProject;
1421
- if (!gcpProject) gcpProject = await input({
1422
- message: "GCP Project ID:",
1423
- validate: (value) => value.length > 0 ? true : "Project ID is required"
1424
- });
1425
- let region = options.region ?? existingConfig?.region ?? "us-central1";
1426
- if (!options.region && !existingConfig?.region) region = await input({
1427
- message: "GCP Region:",
1428
- default: "us-central1"
1429
- });
1430
- let env = options.env ?? existingConfig?.env ?? "dev";
1431
- if (!options.env && !existingConfig?.env) env = await input({
1432
- message: "Environment (dev/prod):",
1433
- default: "dev",
1434
- validate: (value) => value === "dev" || value === "prod" ? true : "Must be dev or prod"
1435
- });
1436
- const stackName = `${gcpProject}-${env}`;
1437
- console.log("\nConfiguration:");
1438
- console.log(` Provider: ${provider}`);
1439
- console.log(` GCP Project: ${gcpProject}`);
1440
- console.log(` Region: ${region}`);
1441
- console.log(` Environment: ${env}`);
1442
- console.log(` Stack: ${stackName}`);
1443
- console.log("");
1444
- if (!options.yes) {
1445
- if (!await confirm({
1446
- message: "Proceed with infrastructure initialization?",
1447
- default: true
1448
- })) {
1449
- console.log("Aborted.");
1450
- process.exit(0);
1451
- }
1452
- }
1453
- console.log("\nInstalling infrastructure dependencies...");
1454
- const { spawn: spawn$1 } = await import("child_process");
1455
- await new Promise((resolve, reject) => {
1456
- spawn$1("bun", ["install"], {
1457
- cwd: infraDir,
1458
- stdio: "inherit",
1459
- shell: true
1460
- }).on("close", (code) => {
1461
- if (code === 0) resolve();
1462
- else reject(/* @__PURE__ */ new Error(`bun install failed with code ${code}`));
1463
- });
1464
- });
1465
- console.log("\nBuilding infrastructure package...");
1466
- await new Promise((resolve, reject) => {
1467
- spawn$1("bun", ["run", "build"], {
1468
- cwd: infraDir,
1469
- stdio: "inherit",
1470
- shell: true
1471
- }).on("close", (code) => {
1472
- if (code === 0) resolve();
1473
- else reject(/* @__PURE__ */ new Error(`Build failed with code ${code}`));
1474
- });
1475
- });
1476
- console.log(`\nSelecting Pulumi stack: ${stackName}`);
1477
- if (!await selectOrCreateStack(stackName, { cwd: infraDir })) {
1478
- console.error("Error: Failed to select or create Pulumi stack");
1479
- process.exit(1);
1480
- }
1481
- console.log("Configuring stack...");
1482
- await setPulumiConfig("gcp:project", gcpProject, { cwd: infraDir });
1483
- await setPulumiConfig("gcp:region", region, { cwd: infraDir });
1484
- await setPulumiConfig("env", env, { cwd: infraDir });
1485
- console.log("\nCreating infrastructure...");
1486
- console.log("This may take several minutes.\n");
1487
- const result = await pulumiUp({
1488
- cwd: infraDir,
1489
- stream: true
1490
- });
1491
- if (result.code !== 0) {
1492
- const parsed = parseError(result.stderr);
1493
- console.error("\n" + formatError(parsed));
1494
- process.exit(1);
1495
- }
1496
- console.log("\nRetrieving outputs...");
1497
- const outputs = await getPulumiOutputs({ cwd: infraDir });
1498
- await saveConfig$1(projectRoot, {
1499
- provider: "gcp",
1500
- gcpProject,
1501
- region,
1502
- projectName: "kuckit-infra",
1503
- stackName,
1504
- env,
1505
- outputs: outputs ? {
1506
- registryUrl: outputs.registryUrl,
1507
- databaseConnectionName: outputs.databaseConnectionName,
1508
- redisHost: outputs.redisHost,
1509
- secretIds: {
1510
- dbPassword: outputs.dbPasswordSecretId,
1511
- redisAuth: outputs.redisAuthSecretId ?? ""
1512
- }
1513
- } : void 0,
1514
- createdAt: existingConfig?.createdAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1515
- updatedAt: (/* @__PURE__ */ new Date()).toISOString()
1516
- });
1517
- console.log(`\nConfiguration saved to ${KUCKIT_DIR$8}/${CONFIG_FILE$8}`);
1518
- console.log("\n" + "=".repeat(60));
1519
- console.log("Infrastructure initialized successfully!");
1520
- console.log("=".repeat(60));
1521
- if (outputs) {
1522
- console.log("\nOutputs:");
1523
- console.log(` Registry: ${outputs.registryUrl}`);
1524
- console.log(` Database: ${outputs.databaseConnectionName}`);
1525
- console.log(` Redis: ${outputs.redisHost}`);
1526
- }
1527
- console.log("\nNext steps:");
1528
- console.log(" 1. Ensure you have a Dockerfile in your project root");
1529
- console.log(` 2. Run: kuckit infra deploy --env ${env}`);
1530
- console.log("");
1531
- }
1532
-
1533
- //#endregion
1534
- //#region src/commands/infra/deploy.ts
1535
- const KUCKIT_DIR$7 = ".kuckit";
1536
- const CONFIG_FILE$7 = "infra.json";
1537
- async function saveConfig(projectRoot, config) {
1538
- const kuckitDir = join$1(projectRoot, KUCKIT_DIR$7);
1539
- await mkdir(kuckitDir, { recursive: true });
1540
- await writeFile(join$1(kuckitDir, CONFIG_FILE$7), JSON.stringify(config, null, 2), "utf-8");
1541
- }
1542
- /**
1543
- * Get the current git commit short hash
1544
- * Returns null if not in a git repo or git command fails
1545
- */
1546
- function getGitShortHash() {
1547
- try {
1548
- return execSync("git rev-parse --short HEAD", { encoding: "utf-8" }).trim();
1549
- } catch {
1550
- return null;
1551
- }
1552
- }
1553
- async function fileExists$7(path) {
1554
- try {
1555
- await access(path, constants$1.F_OK);
1556
- return true;
1557
- } catch {
1558
- return false;
1559
- }
1560
- }
1561
- async function findProjectRoot$7(cwd) {
1562
- let dir = cwd;
1563
- while (dir !== dirname$1(dir)) {
1564
- if (await fileExists$7(join$1(dir, "package.json"))) return dir;
1565
- dir = dirname$1(dir);
1566
- }
1567
- return null;
1568
- }
1569
- async function loadConfig$7(projectRoot) {
1570
- const configPath = join$1(projectRoot, KUCKIT_DIR$7, CONFIG_FILE$7);
1571
- if (!await fileExists$7(configPath)) return null;
1572
- try {
1573
- const content = await readFile(configPath, "utf-8");
1574
- return JSON.parse(content);
1575
- } catch {
1576
- return null;
1577
- }
1578
- }
1579
- async function infraDeploy(options) {
1580
- console.log("Deploying kuckit application...\n");
1581
- if (!await checkPulumiInstalled()) {
1582
- console.error("Error: Pulumi CLI is not installed.");
1583
- console.error("Install it from: https://www.pulumi.com/docs/install/");
1584
- process.exit(1);
1585
- }
1586
- if (!await checkGcloudInstalled()) {
1587
- console.error("Error: gcloud CLI is not installed.");
1588
- console.error("Install it from: https://cloud.google.com/sdk/docs/install");
1589
- process.exit(1);
1590
- }
1591
- const projectRoot = await findProjectRoot$7(process.cwd());
1592
- if (!projectRoot) {
1593
- console.error("Error: Could not find project root (no package.json found)");
1594
- process.exit(1);
1595
- }
1596
- const config = await loadConfig$7(projectRoot);
1597
- if (!config) {
1598
- console.error("Error: No infrastructure configuration found.");
1599
- console.error("Run: kuckit infra init");
1600
- process.exit(1);
1601
- }
1602
- const env = options.env ?? config.env;
1603
- if (env !== "dev" && env !== "prod") {
1604
- console.error(`Error: Invalid environment '${env}'. Must be 'dev' or 'prod'.`);
1605
- process.exit(1);
1606
- }
1607
- const infraDir = getInfraDir(projectRoot);
1608
- if (!await fileExists$7(infraDir)) {
1609
- console.error("Error: packages/infra not found.");
1610
- process.exit(1);
1611
- }
1612
- const dockerfilePath = join$1(projectRoot, "Dockerfile");
1613
- if (!options.skipBuild && !options.image && !await fileExists$7(dockerfilePath)) {
1614
- console.error("Error: Dockerfile not found in project root.");
1615
- console.error("Create a Dockerfile or use --skip-build with an existing image.");
1616
- process.exit(1);
1617
- }
1618
- if (!config.outputs?.registryUrl) {
1619
- console.error("Error: No registry URL found in configuration.");
1620
- console.error("Run: kuckit infra init");
1621
- process.exit(1);
1622
- }
1623
- console.log("Configuration:");
1624
- console.log(` Project: ${config.gcpProject}`);
1625
- console.log(` Region: ${config.region}`);
1626
- console.log(` Environment: ${env}`);
1627
- console.log(` Registry: ${config.outputs.registryUrl}`);
1628
- if (options.preview) console.log(" Mode: Preview (no changes will be applied)");
1629
- console.log("");
1630
- let imageUrl;
1631
- if (options.image) {
1632
- imageUrl = options.image;
1633
- console.log(`Using provided image: ${imageUrl}\n`);
1634
- } else if (options.skipBuild) {
1635
- imageUrl = `${config.outputs.registryUrl}/kuckit:latest`;
1636
- console.log(`Using existing image: ${imageUrl}\n`);
1637
- } else {
1638
- console.log("Building and pushing Docker image...");
1639
- console.log("(Using Cloud Build - no local Docker required)\n");
1640
- const buildResult = await buildAndPushImage({
1641
- project: config.gcpProject,
1642
- registryUrl: config.outputs.registryUrl,
1643
- tag: process.env.GIT_SHA ?? process.env.GITHUB_SHA ?? getGitShortHash() ?? "latest",
1644
- cwd: projectRoot
1645
- });
1646
- if (buildResult.code !== 0) {
1647
- console.error("\nError: Docker build failed.");
1648
- process.exit(1);
1649
- }
1650
- imageUrl = buildResult.imageUrl;
1651
- console.log(`\nImage built: ${imageUrl}\n`);
1652
- }
1653
- if (!options.yes && !options.preview) {
1654
- if (!await confirm({
1655
- message: "Proceed with deployment?",
1656
- default: true
1657
- })) {
1658
- console.log("Aborted.");
1659
- process.exit(0);
1660
- }
1661
- }
1662
- const stackName = config.stackName;
1663
- console.log(`Selecting Pulumi stack: ${stackName}`);
1664
- if (!await selectOrCreateStack(stackName, { cwd: infraDir })) {
1665
- console.error("Error: Failed to select Pulumi stack");
1666
- process.exit(1);
1667
- }
1668
- console.log("Configuring deployment...");
1669
- await setPulumiConfig("imageUrl", imageUrl, { cwd: infraDir });
1670
- if (config.outputs?.serviceUrl) await setPulumiConfig("appUrl", config.outputs.serviceUrl, { cwd: infraDir });
1671
- if (options.preview) console.log("\nPreviewing changes...\n");
1672
- else console.log("\nDeploying to Cloud Run...\n");
1673
- const result = await pulumiUp({
1674
- cwd: infraDir,
1675
- stream: true,
1676
- preview: options.preview
1677
- });
1678
- if (result.code !== 0) {
1679
- const parsed = parseError(result.stderr);
1680
- console.error("\n" + formatError(parsed));
1681
- process.exit(1);
1682
- }
1683
- if (!options.preview) {
1684
- console.log("\nRetrieving deployment info...");
1685
- const outputs = await getPulumiOutputs({ cwd: infraDir });
1686
- if (outputs) {
1687
- config.outputs = {
1688
- ...config.outputs,
1689
- ...outputs
1690
- };
1691
- config.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1692
- await saveConfig(projectRoot, config);
1693
- }
1694
- console.log("\n" + "=".repeat(60));
1695
- console.log("Deployment successful!");
1696
- console.log("=".repeat(60));
1697
- if (outputs?.serviceUrl) console.log(`\nService URL: ${outputs.serviceUrl}`);
1698
- if (outputs?.migrationJobName) console.log(`Migration Job: ${outputs.migrationJobName}`);
1699
- console.log("\nUseful commands:");
1700
- console.log(` View logs: kuckit infra logs --env ${env}`);
1701
- console.log(` Check status: kuckit infra status --env ${env}`);
1702
- console.log(` Run migrations: kuckit infra db:migrate --env ${env}`);
1703
- console.log("");
1704
- } else {
1705
- console.log("\nPreview complete. No changes were applied.");
1706
- console.log("Run without --preview to apply changes.");
1707
- }
1708
- }
1709
-
1710
2058
  //#endregion
1711
2059
  //#region src/commands/infra/destroy.ts
1712
2060
  const KUCKIT_DIR$6 = ".kuckit";
@@ -2673,6 +3021,10 @@ db.command("studio").description("Open Drizzle Studio with all module schemas").
2673
3021
  });
2674
3022
  registerAuthCommands(program);
2675
3023
  const infra = program.command("infra").description("Infrastructure deployment and management");
3024
+ infra.command("up").description("Initialize (if needed) and deploy infrastructure in one command").option("-p, --provider <provider>", "Cloud provider (gcp)", "gcp").option("--project <id>", "GCP project ID").option("--region <region>", "Deployment region", "us-central1").option("-e, --env <env>", "Environment (dev, prod)", "dev").option("--preview", "Preview changes without applying", false).option("-y, --yes", "Skip confirmation prompts", false).action(async (options) => {
3025
+ requireAuth();
3026
+ await infraUp(options);
3027
+ });
2676
3028
  infra.command("init").description("Initialize base infrastructure (no Docker required)").option("-p, --provider <provider>", "Cloud provider (gcp)", "gcp").option("--project <id>", "GCP project ID").option("--region <region>", "Deployment region", "us-central1").option("-e, --env <env>", "Environment (dev, prod)", "dev").option("-y, --yes", "Skip confirmation prompts", false).action(async (options) => {
2677
3029
  requireAuth();
2678
3030
  await infraInit(options);
@@ -2681,6 +3033,10 @@ infra.command("deploy").description("Build and deploy application to Cloud Run")
2681
3033
  requireAuth();
2682
3034
  await infraDeploy(options);
2683
3035
  });
3036
+ infra.command("eject").description("Eject provider infrastructure code to local project for customization").option("-d, --dir <directory>", "Target directory for ejected code", "infra").option("--force", "Overwrite existing directory", false).action(async (options) => {
3037
+ requireAuth();
3038
+ await infraEject(options);
3039
+ });
2684
3040
  infra.command("destroy").description("Destroy infrastructure resources").option("-e, --env <env>", "Environment (dev, prod)", "dev").option("--app-only", "Only destroy Cloud Run, keep DB/Redis", false).option("--force", "Skip confirmation prompt", false).action(async (options) => {
2685
3041
  requireAuth();
2686
3042
  await infraDestroy(options);
@@ -2716,4 +3072,5 @@ infra.command("outputs").description("Display infrastructure outputs (URLs, conn
2716
3072
  program.parse();
2717
3073
 
2718
3074
  //#endregion
2719
- export { };
3075
+ export { };
3076
+ //# sourceMappingURL=bin.js.map