@geekmidas/cli 0.44.0 → 0.46.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.
Files changed (48) hide show
  1. package/dist/{config-C0b0jdmU.mjs → config-C3LSBNSl.mjs} +2 -2
  2. package/dist/{config-C0b0jdmU.mjs.map → config-C3LSBNSl.mjs.map} +1 -1
  3. package/dist/{config-xVZsRjN7.cjs → config-HYiM3iQJ.cjs} +2 -2
  4. package/dist/{config-xVZsRjN7.cjs.map → config-HYiM3iQJ.cjs.map} +1 -1
  5. package/dist/config.cjs +2 -2
  6. package/dist/config.d.cts +1 -1
  7. package/dist/config.d.mts +1 -1
  8. package/dist/config.mjs +2 -2
  9. package/dist/dokploy-api-C1JgU9Vr.mjs +3 -0
  10. package/dist/dokploy-api-Cpq_tLSz.cjs +3 -0
  11. package/dist/{dokploy-api-BdxOMH_V.cjs → dokploy-api-D8a0eQQB.cjs} +110 -1
  12. package/dist/dokploy-api-D8a0eQQB.cjs.map +1 -0
  13. package/dist/{dokploy-api-DWsqNjwP.mjs → dokploy-api-b6usLLKk.mjs} +110 -1
  14. package/dist/dokploy-api-b6usLLKk.mjs.map +1 -0
  15. package/dist/{index-CXa3odEw.d.mts → index-BtnjoghR.d.mts} +540 -46
  16. package/dist/index-BtnjoghR.d.mts.map +1 -0
  17. package/dist/{index-E8Nu2Rxl.d.cts → index-c89X2mi2.d.cts} +540 -46
  18. package/dist/index-c89X2mi2.d.cts.map +1 -0
  19. package/dist/index.cjs +254 -131
  20. package/dist/index.cjs.map +1 -1
  21. package/dist/index.mjs +254 -131
  22. package/dist/index.mjs.map +1 -1
  23. package/dist/{openapi-D3pA6FfZ.mjs → openapi-C3C-BzIZ.mjs} +2 -2
  24. package/dist/{openapi-D3pA6FfZ.mjs.map → openapi-C3C-BzIZ.mjs.map} +1 -1
  25. package/dist/{openapi-DhcCtKzM.cjs → openapi-D7WwlpPF.cjs} +2 -2
  26. package/dist/{openapi-DhcCtKzM.cjs.map → openapi-D7WwlpPF.cjs.map} +1 -1
  27. package/dist/openapi.cjs +3 -3
  28. package/dist/openapi.mjs +3 -3
  29. package/dist/workspace/index.cjs +1 -1
  30. package/dist/workspace/index.d.cts +1 -1
  31. package/dist/workspace/index.d.mts +1 -1
  32. package/dist/workspace/index.mjs +1 -1
  33. package/dist/{workspace-BDAhr6Kb.cjs → workspace-CaVW6j2q.cjs} +10 -1
  34. package/dist/{workspace-BDAhr6Kb.cjs.map → workspace-CaVW6j2q.cjs.map} +1 -1
  35. package/dist/{workspace-D_6ZCaR_.mjs → workspace-DLFRaDc-.mjs} +10 -1
  36. package/dist/{workspace-D_6ZCaR_.mjs.map → workspace-DLFRaDc-.mjs.map} +1 -1
  37. package/package.json +4 -4
  38. package/src/deploy/dokploy-api.ts +163 -0
  39. package/src/deploy/index.ts +313 -225
  40. package/src/deploy/state.ts +146 -0
  41. package/src/workspace/types.ts +566 -47
  42. package/tsconfig.tsbuildinfo +1 -1
  43. package/dist/dokploy-api-Bdmk5ImW.cjs +0 -3
  44. package/dist/dokploy-api-BdxOMH_V.cjs.map +0 -1
  45. package/dist/dokploy-api-DWsqNjwP.mjs.map +0 -1
  46. package/dist/dokploy-api-tZSZaHd9.mjs +0 -3
  47. package/dist/index-CXa3odEw.d.mts.map +0 -1
  48. package/dist/index-E8Nu2Rxl.d.cts.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env -S npx tsx
2
- import { __require, getAppBuildOrder, getDependencyEnvVars, getDeployTargetError, isDeployTargetSupported } from "./workspace-D_6ZCaR_.mjs";
3
- import { getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-C0b0jdmU.mjs";
4
- import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, OpenApiTsGenerator, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-D3pA6FfZ.mjs";
2
+ import { __require, getAppBuildOrder, getDependencyEnvVars, getDeployTargetError, isDeployTargetSupported } from "./workspace-DLFRaDc-.mjs";
3
+ import { getAppNameFromCwd, loadAppConfig, loadConfig, loadWorkspaceConfig, parseModuleConfig } from "./config-C3LSBNSl.mjs";
4
+ import { ConstructGenerator, EndpointGenerator, OPENAPI_OUTPUT_PATH, OpenApiTsGenerator, generateOpenApi, openapiCommand, resolveOpenApiConfig } from "./openapi-C3C-BzIZ.mjs";
5
5
  import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecret, toEmbeddableSecrets, writeStageSecrets } from "./storage-Dhst7BhI.mjs";
6
- import { DokployApi } from "./dokploy-api-DWsqNjwP.mjs";
6
+ import { DokployApi } from "./dokploy-api-b6usLLKk.mjs";
7
7
  import { encryptSecrets } from "./encryption-BC4MAODn.mjs";
8
8
  import { generateReactQueryCommand } from "./openapi-react-query-ZoP9DPbY.mjs";
9
9
  import { createRequire } from "node:module";
@@ -28,7 +28,7 @@ import prompts from "prompts";
28
28
 
29
29
  //#region package.json
30
30
  var name = "@geekmidas/cli";
31
- var version = "0.44.0";
31
+ var version = "0.46.0";
32
32
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
33
33
  var private$1 = false;
34
34
  var type = "module";
@@ -227,7 +227,7 @@ const logger$10 = console;
227
227
  * Validate Dokploy token by making a test API call
228
228
  */
229
229
  async function validateDokployToken(endpoint, token) {
230
- const { DokployApi: DokployApi$1 } = await import("./dokploy-api-tZSZaHd9.mjs");
230
+ const { DokployApi: DokployApi$1 } = await import("./dokploy-api-C1JgU9Vr.mjs");
231
231
  const api = new DokployApi$1({
232
232
  baseUrl: endpoint,
233
233
  token
@@ -3810,6 +3810,89 @@ async function deployDokploy(options) {
3810
3810
  };
3811
3811
  }
3812
3812
 
3813
+ //#endregion
3814
+ //#region src/deploy/state.ts
3815
+ /**
3816
+ * Get the state file path for a stage
3817
+ */
3818
+ function getStateFilePath(workspaceRoot, stage) {
3819
+ return join(workspaceRoot, ".gkm", `deploy-${stage}.json`);
3820
+ }
3821
+ /**
3822
+ * Read the deploy state for a stage
3823
+ * Returns null if state file doesn't exist
3824
+ */
3825
+ async function readStageState(workspaceRoot, stage) {
3826
+ const filePath = getStateFilePath(workspaceRoot, stage);
3827
+ try {
3828
+ const content = await readFile(filePath, "utf-8");
3829
+ return JSON.parse(content);
3830
+ } catch (error) {
3831
+ if (error.code === "ENOENT") return null;
3832
+ console.warn(`Warning: Could not read deploy state: ${error}`);
3833
+ return null;
3834
+ }
3835
+ }
3836
+ /**
3837
+ * Write the deploy state for a stage
3838
+ */
3839
+ async function writeStageState(workspaceRoot, stage, state) {
3840
+ const filePath = getStateFilePath(workspaceRoot, stage);
3841
+ const dir = join(workspaceRoot, ".gkm");
3842
+ await mkdir(dir, { recursive: true });
3843
+ state.lastDeployedAt = (/* @__PURE__ */ new Date()).toISOString();
3844
+ await writeFile(filePath, JSON.stringify(state, null, 2));
3845
+ }
3846
+ /**
3847
+ * Create a new empty state for a stage
3848
+ */
3849
+ function createEmptyState(stage, environmentId) {
3850
+ return {
3851
+ provider: "dokploy",
3852
+ stage,
3853
+ environmentId,
3854
+ applications: {},
3855
+ services: {},
3856
+ lastDeployedAt: (/* @__PURE__ */ new Date()).toISOString()
3857
+ };
3858
+ }
3859
+ /**
3860
+ * Get application ID from state
3861
+ */
3862
+ function getApplicationId(state, appName) {
3863
+ return state?.applications[appName];
3864
+ }
3865
+ /**
3866
+ * Set application ID in state (mutates state)
3867
+ */
3868
+ function setApplicationId(state, appName, applicationId) {
3869
+ state.applications[appName] = applicationId;
3870
+ }
3871
+ /**
3872
+ * Get postgres ID from state
3873
+ */
3874
+ function getPostgresId(state) {
3875
+ return state?.services.postgresId;
3876
+ }
3877
+ /**
3878
+ * Set postgres ID in state (mutates state)
3879
+ */
3880
+ function setPostgresId(state, postgresId) {
3881
+ state.services.postgresId = postgresId;
3882
+ }
3883
+ /**
3884
+ * Get redis ID from state
3885
+ */
3886
+ function getRedisId(state) {
3887
+ return state?.services.redisId;
3888
+ }
3889
+ /**
3890
+ * Set redis ID in state (mutates state)
3891
+ */
3892
+ function setRedisId(state, redisId) {
3893
+ state.services.redisId = redisId;
3894
+ }
3895
+
3813
3896
  //#endregion
3814
3897
  //#region src/deploy/domain.ts
3815
3898
  /**
@@ -4314,24 +4397,39 @@ async function prompt(message, hidden = false) {
4314
4397
  * Provision docker compose services in Dokploy
4315
4398
  * @internal Exported for testing
4316
4399
  */
4317
- async function provisionServices(api, projectId, environmentId, appName, services, existingUrls) {
4400
+ async function provisionServices(api, projectId, environmentId, appName, services, existingServiceIds) {
4318
4401
  logger$1.log(`\n🔍 provisionServices called: services=${JSON.stringify(services)}, envId=${environmentId}`);
4319
4402
  if (!services || !environmentId) {
4320
4403
  logger$1.log(" Skipping: no services or no environmentId");
4321
4404
  return void 0;
4322
4405
  }
4323
4406
  const serviceUrls = {};
4324
- if (services.postgres) if (existingUrls?.DATABASE_URL) logger$1.log("\n🐘 PostgreSQL: Already configured (skipping)");
4325
- else {
4326
- logger$1.log("\n🐘 Provisioning PostgreSQL...");
4327
- const postgresName = `${appName}-db`;
4407
+ const serviceIds = {};
4408
+ if (services.postgres) {
4409
+ logger$1.log("\n🐘 Checking PostgreSQL...");
4410
+ const postgresName = "db";
4328
4411
  try {
4329
- const { randomBytes: randomBytes$1 } = await import("node:crypto");
4330
- const databasePassword = randomBytes$1(16).toString("hex");
4331
- const postgres = await api.createPostgres(postgresName, projectId, environmentId, { databasePassword });
4332
- logger$1.log(` Created PostgreSQL: ${postgres.postgresId}`);
4333
- await api.deployPostgres(postgres.postgresId);
4334
- logger$1.log(" ✓ PostgreSQL deployed");
4412
+ let postgres = null;
4413
+ let created = false;
4414
+ if (existingServiceIds?.postgresId) {
4415
+ logger$1.log(` Using cached ID: ${existingServiceIds.postgresId}`);
4416
+ postgres = await api.getPostgres(existingServiceIds.postgresId);
4417
+ if (postgres) logger$1.log(` ✓ PostgreSQL found: ${postgres.postgresId}`);
4418
+ else logger$1.log(` ⚠ Cached ID invalid, will create new`);
4419
+ }
4420
+ if (!postgres) {
4421
+ const { randomBytes: randomBytes$1 } = await import("node:crypto");
4422
+ const databasePassword = randomBytes$1(16).toString("hex");
4423
+ const result = await api.findOrCreatePostgres(postgresName, projectId, environmentId, { databasePassword });
4424
+ postgres = result.postgres;
4425
+ created = result.created;
4426
+ if (created) {
4427
+ logger$1.log(` ✓ Created PostgreSQL: ${postgres.postgresId}`);
4428
+ await api.deployPostgres(postgres.postgresId);
4429
+ logger$1.log(" ✓ PostgreSQL deployed");
4430
+ } else logger$1.log(` ✓ PostgreSQL already exists: ${postgres.postgresId}`);
4431
+ }
4432
+ serviceIds.postgresId = postgres.postgresId;
4335
4433
  serviceUrls.DATABASE_HOST = postgres.appName;
4336
4434
  serviceUrls.DATABASE_PORT = "5432";
4337
4435
  serviceUrls.DATABASE_NAME = postgres.databaseName;
@@ -4341,21 +4439,34 @@ async function provisionServices(api, projectId, environmentId, appName, service
4341
4439
  logger$1.log(` ✓ Database credentials configured`);
4342
4440
  } catch (error) {
4343
4441
  const message = error instanceof Error ? error.message : "Unknown error";
4344
- if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` PostgreSQL already exists`);
4345
- else logger$1.log(` ⚠ Failed to provision PostgreSQL: ${message}`);
4442
+ logger$1.log(` Failed to provision PostgreSQL: ${message}`);
4346
4443
  }
4347
4444
  }
4348
- if (services.redis) if (existingUrls?.REDIS_URL) logger$1.log("\n🔴 Redis: Already configured (skipping)");
4349
- else {
4350
- logger$1.log("\n🔴 Provisioning Redis...");
4351
- const redisName = `${appName}-cache`;
4445
+ if (services.redis) {
4446
+ logger$1.log("\n🔴 Checking Redis...");
4447
+ const redisName = "cache";
4352
4448
  try {
4353
- const { randomBytes: randomBytes$1 } = await import("node:crypto");
4354
- const databasePassword = randomBytes$1(16).toString("hex");
4355
- const redis = await api.createRedis(redisName, projectId, environmentId, { databasePassword });
4356
- logger$1.log(` Created Redis: ${redis.redisId}`);
4357
- await api.deployRedis(redis.redisId);
4358
- logger$1.log(" ✓ Redis deployed");
4449
+ let redis = null;
4450
+ let created = false;
4451
+ if (existingServiceIds?.redisId) {
4452
+ logger$1.log(` Using cached ID: ${existingServiceIds.redisId}`);
4453
+ redis = await api.getRedis(existingServiceIds.redisId);
4454
+ if (redis) logger$1.log(` ✓ Redis found: ${redis.redisId}`);
4455
+ else logger$1.log(` ⚠ Cached ID invalid, will create new`);
4456
+ }
4457
+ if (!redis) {
4458
+ const { randomBytes: randomBytes$1 } = await import("node:crypto");
4459
+ const databasePassword = randomBytes$1(16).toString("hex");
4460
+ const result = await api.findOrCreateRedis(redisName, projectId, environmentId, { databasePassword });
4461
+ redis = result.redis;
4462
+ created = result.created;
4463
+ if (created) {
4464
+ logger$1.log(` ✓ Created Redis: ${redis.redisId}`);
4465
+ await api.deployRedis(redis.redisId);
4466
+ logger$1.log(" ✓ Redis deployed");
4467
+ } else logger$1.log(` ✓ Redis already exists: ${redis.redisId}`);
4468
+ }
4469
+ serviceIds.redisId = redis.redisId;
4359
4470
  serviceUrls.REDIS_HOST = redis.appName;
4360
4471
  serviceUrls.REDIS_PORT = "6379";
4361
4472
  if (redis.databasePassword) serviceUrls.REDIS_PASSWORD = redis.databasePassword;
@@ -4364,11 +4475,13 @@ async function provisionServices(api, projectId, environmentId, appName, service
4364
4475
  logger$1.log(` ✓ Redis credentials configured`);
4365
4476
  } catch (error) {
4366
4477
  const message = error instanceof Error ? error.message : "Unknown error";
4367
- if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` Redis already exists`);
4368
- else logger$1.log(` ⚠ Failed to provision Redis: ${message}`);
4478
+ logger$1.log(` Failed to provision Redis: ${message}`);
4369
4479
  }
4370
4480
  }
4371
- return Object.keys(serviceUrls).length > 0 ? serviceUrls : void 0;
4481
+ return Object.keys(serviceUrls).length > 0 ? {
4482
+ serviceUrls,
4483
+ serviceIds
4484
+ } : void 0;
4372
4485
  }
4373
4486
  /**
4374
4487
  * Ensure Dokploy is fully configured, recovering/creating resources as needed
@@ -4423,7 +4536,7 @@ async function ensureDokploySetup(config$1, dockerConfig, stage, services) {
4423
4536
  }
4424
4537
  const environmentId$1 = environment.environmentId;
4425
4538
  logger$1.log(` Services config: ${JSON.stringify(services)}, envId: ${environmentId$1}`);
4426
- const serviceUrls$1 = await provisionServices(api, existingConfig.projectId, environmentId$1, dockerConfig.appName, services, existingUrls);
4539
+ const provisionResult$1 = await provisionServices(api, existingConfig.projectId, environmentId$1, dockerConfig.appName, services, void 0);
4427
4540
  return {
4428
4541
  config: {
4429
4542
  endpoint: existingConfig.endpoint,
@@ -4432,7 +4545,7 @@ async function ensureDokploySetup(config$1, dockerConfig, stage, services) {
4432
4545
  registry: existingConfig.registry,
4433
4546
  registryId: storedRegistryId ?? void 0
4434
4547
  },
4435
- serviceUrls: serviceUrls$1
4548
+ serviceUrls: provisionResult$1?.serviceUrls
4436
4549
  };
4437
4550
  } catch {
4438
4551
  logger$1.log("⚠ Project not found, will recover...");
@@ -4539,10 +4652,10 @@ async function ensureDokploySetup(config$1, dockerConfig, stage, services) {
4539
4652
  logger$1.log(` Project: ${project.projectId}`);
4540
4653
  logger$1.log(` Application: ${applicationId}`);
4541
4654
  if (registryId) logger$1.log(` Registry: ${registryId}`);
4542
- const serviceUrls = await provisionServices(api, project.projectId, environmentId, dockerConfig.appName, services, existingUrls);
4655
+ const provisionResult = await provisionServices(api, project.projectId, environmentId, dockerConfig.appName, services, void 0);
4543
4656
  return {
4544
4657
  config: dokployConfig,
4545
- serviceUrls
4658
+ serviceUrls: provisionResult?.serviceUrls
4546
4659
  };
4547
4660
  }
4548
4661
  /**
@@ -4661,6 +4774,18 @@ async function workspaceDeployCommand(workspace, options) {
4661
4774
  } else environmentId = result.environment.environmentId;
4662
4775
  logger$1.log(` ✓ Created project: ${project.projectId}`);
4663
4776
  }
4777
+ logger$1.log("\n📋 Loading deploy state...");
4778
+ let state = await readStageState(workspace.root, stage);
4779
+ if (state) {
4780
+ logger$1.log(` Found existing state for stage "${stage}"`);
4781
+ if (state.environmentId !== environmentId) {
4782
+ logger$1.log(` ⚠ Environment ID changed, updating state`);
4783
+ state.environmentId = environmentId;
4784
+ }
4785
+ } else {
4786
+ logger$1.log(` Creating new state for stage "${stage}"`);
4787
+ state = createEmptyState(stage, environmentId);
4788
+ }
4664
4789
  logger$1.log("\n🐳 Checking registry...");
4665
4790
  let registryId = await getDokployRegistryId();
4666
4791
  const registry = workspace.deploy.dokploy?.registry;
@@ -4696,7 +4821,15 @@ async function workspaceDeployCommand(workspace, options) {
4696
4821
  };
4697
4822
  if (dockerServices.postgres || dockerServices.redis) {
4698
4823
  logger$1.log("\n🔧 Provisioning infrastructure services...");
4699
- await provisionServices(api, project.projectId, environmentId, workspace.name, dockerServices);
4824
+ const existingServiceIds = {
4825
+ postgresId: getPostgresId(state),
4826
+ redisId: getRedisId(state)
4827
+ };
4828
+ const provisionResult = await provisionServices(api, project.projectId, environmentId, workspace.name, dockerServices, existingServiceIds);
4829
+ if (provisionResult?.serviceIds) {
4830
+ if (provisionResult.serviceIds.postgresId) setPostgresId(state, provisionResult.serviceIds.postgresId);
4831
+ if (provisionResult.serviceIds.redisId) setRedisId(state, provisionResult.serviceIds.redisId);
4832
+ }
4700
4833
  }
4701
4834
  const backendApps = appsToDeployNames.filter((name$1) => workspace.apps[name$1].type === "backend");
4702
4835
  const frontendApps = appsToDeployNames.filter((name$1) => workspace.apps[name$1].type === "frontend");
@@ -4709,16 +4842,22 @@ async function workspaceDeployCommand(workspace, options) {
4709
4842
  const app = workspace.apps[appName];
4710
4843
  logger$1.log(`\n ⚙️ Deploying ${appName}...`);
4711
4844
  try {
4712
- const dokployAppName = `${workspace.name}-${appName}`;
4713
- let application;
4714
- try {
4715
- application = await api.createApplication(dokployAppName, project.projectId, environmentId);
4716
- logger$1.log(` Created application: ${application.applicationId}`);
4717
- } catch (error) {
4718
- const message = error instanceof Error ? error.message : "Unknown error";
4719
- if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` Application already exists`);
4720
- else throw error;
4845
+ const dokployAppName = appName;
4846
+ let application = null;
4847
+ const cachedAppId = getApplicationId(state, appName);
4848
+ if (cachedAppId) {
4849
+ logger$1.log(` Using cached ID: ${cachedAppId}`);
4850
+ application = await api.getApplication(cachedAppId);
4851
+ if (application) logger$1.log(` ✓ Application found: ${application.applicationId}`);
4852
+ else logger$1.log(` Cached ID invalid, will create new`);
4853
+ }
4854
+ if (!application) {
4855
+ const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
4856
+ application = result.application;
4857
+ if (result.created) logger$1.log(` Created application: ${application.applicationId}`);
4858
+ else logger$1.log(` Found existing application: ${application.applicationId}`);
4721
4859
  }
4860
+ setApplicationId(state, appName, application.applicationId);
4722
4861
  const appSecrets = encryptedSecrets.get(appName);
4723
4862
  const buildArgs = [];
4724
4863
  if (appSecrets && appSecrets.secretCount > 0) {
@@ -4742,47 +4881,35 @@ async function workspaceDeployCommand(workspace, options) {
4742
4881
  });
4743
4882
  const envVars = [`NODE_ENV=production`, `PORT=${app.port}`];
4744
4883
  if (appSecrets && appSecrets.masterKey) envVars.push(`GKM_MASTER_KEY=${appSecrets.masterKey}`);
4745
- if (application) {
4746
- await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
4747
- await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
4748
- logger$1.log(` Deploying to Dokploy...`);
4749
- await api.deployApplication(application.applicationId);
4750
- try {
4751
- const host = resolveHost(appName, app, stage, dokployConfig, false);
4752
- await api.createDomain({
4753
- host,
4754
- port: app.port,
4755
- https: true,
4756
- certificateType: "letsencrypt",
4757
- applicationId: application.applicationId
4758
- });
4759
- const publicUrl = `https://${host}`;
4760
- publicUrls[appName] = publicUrl;
4761
- logger$1.log(` ✓ Domain: ${publicUrl}`);
4762
- } catch (domainError) {
4763
- const host = resolveHost(appName, app, stage, dokployConfig, false);
4764
- publicUrls[appName] = `https://${host}`;
4765
- logger$1.log(` ℹ Domain already configured: https://${host}`);
4766
- }
4767
- results.push({
4768
- appName,
4769
- type: app.type,
4770
- success: true,
4771
- applicationId: application.applicationId,
4772
- imageRef
4884
+ await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
4885
+ await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
4886
+ logger$1.log(` Deploying to Dokploy...`);
4887
+ await api.deployApplication(application.applicationId);
4888
+ try {
4889
+ const host = resolveHost(appName, app, stage, dokployConfig, false);
4890
+ await api.createDomain({
4891
+ host,
4892
+ port: app.port,
4893
+ https: true,
4894
+ certificateType: "letsencrypt",
4895
+ applicationId: application.applicationId
4773
4896
  });
4774
- logger$1.log(` ✓ ${appName} deployed successfully`);
4775
- } else {
4897
+ const publicUrl = `https://${host}`;
4898
+ publicUrls[appName] = publicUrl;
4899
+ logger$1.log(` ✓ Domain: ${publicUrl}`);
4900
+ } catch (domainError) {
4776
4901
  const host = resolveHost(appName, app, stage, dokployConfig, false);
4777
4902
  publicUrls[appName] = `https://${host}`;
4778
- results.push({
4779
- appName,
4780
- type: app.type,
4781
- success: true,
4782
- imageRef
4783
- });
4784
- logger$1.log(` ✓ ${appName} image pushed (app already exists)`);
4903
+ logger$1.log(` ℹ Domain already configured: https://${host}`);
4785
4904
  }
4905
+ results.push({
4906
+ appName,
4907
+ type: app.type,
4908
+ success: true,
4909
+ applicationId: application.applicationId,
4910
+ imageRef
4911
+ });
4912
+ logger$1.log(` ✓ ${appName} deployed successfully`);
4786
4913
  } catch (error) {
4787
4914
  const message = error instanceof Error ? error.message : "Unknown error";
4788
4915
  logger$1.log(` ✗ Failed to deploy ${appName}: ${message}`);
@@ -4802,16 +4929,22 @@ async function workspaceDeployCommand(workspace, options) {
4802
4929
  const app = workspace.apps[appName];
4803
4930
  logger$1.log(`\n 🌐 Deploying ${appName}...`);
4804
4931
  try {
4805
- const dokployAppName = `${workspace.name}-${appName}`;
4806
- let application;
4807
- try {
4808
- application = await api.createApplication(dokployAppName, project.projectId, environmentId);
4809
- logger$1.log(` Created application: ${application.applicationId}`);
4810
- } catch (error) {
4811
- const message = error instanceof Error ? error.message : "Unknown error";
4812
- if (message.includes("already exists") || message.includes("duplicate")) logger$1.log(` Application already exists`);
4813
- else throw error;
4932
+ const dokployAppName = appName;
4933
+ let application = null;
4934
+ const cachedAppId = getApplicationId(state, appName);
4935
+ if (cachedAppId) {
4936
+ logger$1.log(` Using cached ID: ${cachedAppId}`);
4937
+ application = await api.getApplication(cachedAppId);
4938
+ if (application) logger$1.log(` ✓ Application found: ${application.applicationId}`);
4939
+ else logger$1.log(` Cached ID invalid, will create new`);
4814
4940
  }
4941
+ if (!application) {
4942
+ const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
4943
+ application = result.application;
4944
+ if (result.created) logger$1.log(` Created application: ${application.applicationId}`);
4945
+ else logger$1.log(` Found existing application: ${application.applicationId}`);
4946
+ }
4947
+ setApplicationId(state, appName, application.applicationId);
4815
4948
  const buildArgs = generatePublicUrlBuildArgs(app, publicUrls);
4816
4949
  if (buildArgs.length > 0) logger$1.log(` Public URLs: ${buildArgs.join(", ")}`);
4817
4950
  const imageName = `${workspace.name}-${appName}`;
@@ -4830,49 +4963,36 @@ async function workspaceDeployCommand(workspace, options) {
4830
4963
  publicUrlArgs: getPublicUrlArgNames(app)
4831
4964
  });
4832
4965
  const envVars = [`NODE_ENV=production`, `PORT=${app.port}`];
4833
- if (application) {
4834
- await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
4835
- await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
4836
- logger$1.log(` Deploying to Dokploy...`);
4837
- await api.deployApplication(application.applicationId);
4838
- const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
4839
- try {
4840
- const host = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
4841
- await api.createDomain({
4842
- host,
4843
- port: app.port,
4844
- https: true,
4845
- certificateType: "letsencrypt",
4846
- applicationId: application.applicationId
4847
- });
4848
- const publicUrl = `https://${host}`;
4849
- publicUrls[appName] = publicUrl;
4850
- logger$1.log(` ✓ Domain: ${publicUrl}`);
4851
- } catch (domainError) {
4852
- const host = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
4853
- publicUrls[appName] = `https://${host}`;
4854
- logger$1.log(` ℹ Domain already configured: https://${host}`);
4855
- }
4856
- results.push({
4857
- appName,
4858
- type: app.type,
4859
- success: true,
4860
- applicationId: application.applicationId,
4861
- imageRef
4966
+ await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
4967
+ await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
4968
+ logger$1.log(` Deploying to Dokploy...`);
4969
+ await api.deployApplication(application.applicationId);
4970
+ const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
4971
+ try {
4972
+ const host = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
4973
+ await api.createDomain({
4974
+ host,
4975
+ port: app.port,
4976
+ https: true,
4977
+ certificateType: "letsencrypt",
4978
+ applicationId: application.applicationId
4862
4979
  });
4863
- logger$1.log(` ✓ ${appName} deployed successfully`);
4864
- } else {
4865
- const isMainFrontend = isMainFrontendApp(appName, app, workspace.apps);
4980
+ const publicUrl = `https://${host}`;
4981
+ publicUrls[appName] = publicUrl;
4982
+ logger$1.log(` ✓ Domain: ${publicUrl}`);
4983
+ } catch (domainError) {
4866
4984
  const host = resolveHost(appName, app, stage, dokployConfig, isMainFrontend);
4867
4985
  publicUrls[appName] = `https://${host}`;
4868
- results.push({
4869
- appName,
4870
- type: app.type,
4871
- success: true,
4872
- imageRef
4873
- });
4874
- logger$1.log(` ✓ ${appName} image pushed (app already exists)`);
4986
+ logger$1.log(` ℹ Domain already configured: https://${host}`);
4875
4987
  }
4988
+ results.push({
4989
+ appName,
4990
+ type: app.type,
4991
+ success: true,
4992
+ applicationId: application.applicationId,
4993
+ imageRef
4994
+ });
4995
+ logger$1.log(` ✓ ${appName} deployed successfully`);
4876
4996
  } catch (error) {
4877
4997
  const message = error instanceof Error ? error.message : "Unknown error";
4878
4998
  logger$1.log(` ✗ Failed to deploy ${appName}: ${message}`);
@@ -4885,6 +5005,9 @@ async function workspaceDeployCommand(workspace, options) {
4885
5005
  }
4886
5006
  }
4887
5007
  }
5008
+ logger$1.log("\n📋 Saving deploy state...");
5009
+ await writeStageState(workspace.root, stage, state);
5010
+ logger$1.log(` ✓ State saved to .gkm/deploy-${stage}.json`);
4888
5011
  const successCount = results.filter((r) => r.success).length;
4889
5012
  const failedCount = results.filter((r) => !r.success).length;
4890
5013
  logger$1.log(`\n${"─".repeat(50)}`);