@geekmidas/cli 1.10.8 → 1.10.10

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 (69) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/{bundler-NpfYPBUo.cjs → bundler-Bm3Az_sv.cjs} +2 -2
  3. package/dist/{bundler-NpfYPBUo.cjs.map → bundler-Bm3Az_sv.cjs.map} +1 -1
  4. package/dist/{bundler-DQYjKFPm.mjs → bundler-kk_XJTRp.mjs} +2 -2
  5. package/dist/{bundler-DQYjKFPm.mjs.map → bundler-kk_XJTRp.mjs.map} +1 -1
  6. package/dist/config.d.cts +2 -2
  7. package/dist/config.d.mts +2 -2
  8. package/dist/{fullstack-secrets-BctGaE4E.cjs → fullstack-secrets-BC9t9wWB.cjs} +27 -1
  9. package/dist/fullstack-secrets-BC9t9wWB.cjs.map +1 -0
  10. package/dist/{fullstack-secrets-ca0Kyrvt.mjs → fullstack-secrets-DmUOfLeX.mjs} +16 -2
  11. package/dist/fullstack-secrets-DmUOfLeX.mjs.map +1 -0
  12. package/dist/{index-9tjTQjFt.d.mts → index-BdJZKXCJ.d.cts} +4 -2
  13. package/dist/index-BdJZKXCJ.d.cts.map +1 -0
  14. package/dist/{index-VOKKO-lm.d.cts → index-DB9VbcCD.d.mts} +4 -2
  15. package/dist/index-DB9VbcCD.d.mts.map +1 -0
  16. package/dist/index.cjs +174 -49
  17. package/dist/index.cjs.map +1 -1
  18. package/dist/index.mjs +174 -49
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/openapi-BYxAWwok.cjs.map +1 -1
  21. package/dist/openapi-DenF-okj.mjs.map +1 -1
  22. package/dist/openapi.d.cts +1 -1
  23. package/dist/openapi.d.mts +1 -1
  24. package/dist/{reconcile-TEBsryVn.cjs → reconcile-BZ8j_-0z.cjs} +2 -2
  25. package/dist/{reconcile-TEBsryVn.cjs.map → reconcile-BZ8j_-0z.cjs.map} +1 -1
  26. package/dist/{reconcile-C5OyCA7V.mjs → reconcile-C0dsg-Gq.mjs} +2 -2
  27. package/dist/{reconcile-C5OyCA7V.mjs.map → reconcile-C0dsg-Gq.mjs.map} +1 -1
  28. package/dist/{storage-DmCbr6DI.mjs → storage-B7H2PPCS.mjs} +8 -1
  29. package/dist/{storage-DmCbr6DI.mjs.map → storage-B7H2PPCS.mjs.map} +1 -1
  30. package/dist/{storage-Dx_jZbq6.mjs → storage-C1FNm2EP.mjs} +1 -1
  31. package/dist/{storage-CoCNe0Pt.cjs → storage-Cs13jkJ9.cjs} +8 -1
  32. package/dist/{storage-CoCNe0Pt.cjs.map → storage-Cs13jkJ9.cjs.map} +1 -1
  33. package/dist/{storage-C7pmBq1u.cjs → storage-D6BGLgWf.cjs} +1 -1
  34. package/dist/{sync-6FoT41G3.mjs → sync-CyGe5f1I.mjs} +1 -1
  35. package/dist/{sync-CbeKrnQV.mjs → sync-CzXruMzP.mjs} +2 -2
  36. package/dist/{sync-CbeKrnQV.mjs.map → sync-CzXruMzP.mjs.map} +1 -1
  37. package/dist/sync-DLlwsrBs.cjs +4 -0
  38. package/dist/{sync-DdkKaHqP.cjs → sync-oCqELfeA.cjs} +2 -2
  39. package/dist/{sync-DdkKaHqP.cjs.map → sync-oCqELfeA.cjs.map} +1 -1
  40. package/dist/{types-C7QJJl9f.d.cts → types-D4MLWXSL.d.cts} +2 -2
  41. package/dist/{types-C7QJJl9f.d.cts.map → types-D4MLWXSL.d.cts.map} +1 -1
  42. package/dist/{types-Iqsq_FIG.d.mts → types-DwpLq_fp.d.mts} +2 -2
  43. package/dist/{types-Iqsq_FIG.d.mts.map → types-DwpLq_fp.d.mts.map} +1 -1
  44. package/dist/workspace/index.d.cts +2 -2
  45. package/dist/workspace/index.d.mts +2 -2
  46. package/dist/workspace-4SP3Gx4Y.cjs.map +1 -1
  47. package/dist/workspace-D4z4A4cq.mjs.map +1 -1
  48. package/package.json +3 -3
  49. package/src/dev/__tests__/index.spec.ts +73 -0
  50. package/src/dev/index.ts +42 -26
  51. package/src/docker/__tests__/compose.spec.ts +145 -1
  52. package/src/docker/compose.ts +97 -5
  53. package/src/init/index.ts +1 -0
  54. package/src/init/versions.ts +1 -1
  55. package/src/secrets/__tests__/generator.spec.ts +68 -0
  56. package/src/secrets/__tests__/storage.spec.ts +30 -0
  57. package/src/secrets/generator.ts +18 -0
  58. package/src/secrets/index.ts +9 -0
  59. package/src/secrets/storage.ts +7 -0
  60. package/src/secrets/types.ts +4 -0
  61. package/src/setup/__tests__/reconcile-secrets.spec.ts +59 -0
  62. package/src/setup/index.ts +52 -16
  63. package/src/types.ts +1 -1
  64. package/src/workspace/types.ts +2 -0
  65. package/dist/fullstack-secrets-BctGaE4E.cjs.map +0 -1
  66. package/dist/fullstack-secrets-ca0Kyrvt.mjs.map +0 -1
  67. package/dist/index-9tjTQjFt.d.mts.map +0 -1
  68. package/dist/index-VOKKO-lm.d.cts.map +0 -1
  69. package/dist/sync-RsnjXYwG.cjs +0 -4
package/dist/index.cjs CHANGED
@@ -4,13 +4,13 @@ const require_workspace = require('./workspace-4SP3Gx4Y.cjs');
4
4
  const require_config = require('./config-D3ORuiUs.cjs');
5
5
  const require_credentials = require('./credentials-C8DWtnMY.cjs');
6
6
  const require_openapi = require('./openapi-BYxAWwok.cjs');
7
- const require_storage = require('./storage-CoCNe0Pt.cjs');
7
+ const require_storage = require('./storage-Cs13jkJ9.cjs');
8
8
  const require_dokploy_api = require('./dokploy-api-DLgvEQlr.cjs');
9
9
  const require_encryption = require('./encryption-BE0UOb8j.cjs');
10
10
  const require_CachedStateProvider = require('./CachedStateProvider-D73dCqfH.cjs');
11
- const require_fullstack_secrets = require('./fullstack-secrets-BctGaE4E.cjs');
11
+ const require_fullstack_secrets = require('./fullstack-secrets-BC9t9wWB.cjs');
12
12
  const require_openapi_react_query = require('./openapi-react-query-DYbBq-WJ.cjs');
13
- const require_sync = require('./sync-DdkKaHqP.cjs');
13
+ const require_sync = require('./sync-oCqELfeA.cjs');
14
14
  const node_fs = require_chunk.__toESM(require("node:fs"));
15
15
  const node_path = require_chunk.__toESM(require("node:path"));
16
16
  const commander = require_chunk.__toESM(require("commander"));
@@ -35,7 +35,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
35
35
 
36
36
  //#region package.json
37
37
  var name = "@geekmidas/cli";
38
- var version = "1.10.7";
38
+ var version = "1.10.9";
39
39
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
40
40
  var private$1 = false;
41
41
  var type = "module";
@@ -767,25 +767,30 @@ async function resolveServicePorts(workspaceRoot) {
767
767
  const savedState = await loadPortState(workspaceRoot);
768
768
  const dockerEnv = {};
769
769
  const ports = {};
770
+ const assignedPorts = /* @__PURE__ */ new Set();
770
771
  logger$11.log("\n🔌 Resolving service ports...");
771
772
  for (const mapping of mappings) {
772
773
  const containerPort = getContainerHostPort(workspaceRoot, mapping.service, mapping.containerPort);
773
774
  if (containerPort !== null) {
774
775
  ports[mapping.envVar] = containerPort;
775
776
  dockerEnv[mapping.envVar] = String(containerPort);
777
+ assignedPorts.add(containerPort);
776
778
  logger$11.log(` 🔄 ${mapping.service}:${mapping.containerPort}: reusing existing container on port ${containerPort}`);
777
779
  continue;
778
780
  }
779
781
  const savedPort = savedState[mapping.envVar];
780
- if (savedPort && await isPortAvailable(savedPort)) {
782
+ if (savedPort && !assignedPorts.has(savedPort) && await isPortAvailable(savedPort)) {
781
783
  ports[mapping.envVar] = savedPort;
782
784
  dockerEnv[mapping.envVar] = String(savedPort);
785
+ assignedPorts.add(savedPort);
783
786
  logger$11.log(` 💾 ${mapping.service}:${mapping.containerPort}: using saved port ${savedPort}`);
784
787
  continue;
785
788
  }
786
- const resolvedPort = await findAvailablePort(mapping.defaultPort);
789
+ let resolvedPort = await findAvailablePort(mapping.defaultPort);
790
+ while (assignedPorts.has(resolvedPort)) resolvedPort = await findAvailablePort(resolvedPort + 1);
787
791
  ports[mapping.envVar] = resolvedPort;
788
792
  dockerEnv[mapping.envVar] = String(resolvedPort);
793
+ assignedPorts.add(resolvedPort);
789
794
  if (resolvedPort !== mapping.defaultPort) logger$11.log(` ⚡ ${mapping.service}:${mapping.containerPort}: port ${mapping.defaultPort} occupied, using port ${resolvedPort}`);
790
795
  else logger$11.log(` ✅ ${mapping.service}:${mapping.containerPort}: using default port ${resolvedPort}`);
791
796
  }
@@ -1235,26 +1240,31 @@ function buildDockerComposeEnv(secretsEnv, portEnv) {
1235
1240
  };
1236
1241
  }
1237
1242
  /**
1243
+ * Parse all service names from a docker-compose.yml file.
1244
+ * @internal Exported for testing
1245
+ */
1246
+ function parseComposeServiceNames(composePath) {
1247
+ if (!(0, node_fs.existsSync)(composePath)) return [];
1248
+ const content = (0, node_fs.readFileSync)(composePath, "utf-8");
1249
+ const compose = (0, yaml.parse)(content);
1250
+ return Object.keys(compose?.services ?? {});
1251
+ }
1252
+ /**
1238
1253
  * Start docker-compose services for the workspace.
1239
- * Passes both port mappings and secrets to docker-compose so that
1240
- * variables like POSTGRES_USER/POSTGRES_PASSWORD are available.
1254
+ * Parses the docker-compose.yml to discover all services and starts
1255
+ * everything except app services (which are managed by turbo).
1256
+ * This ensures manually added services are always started.
1241
1257
  * @internal Exported for testing
1242
1258
  */
1243
1259
  async function startWorkspaceServices(workspace, portEnv, secretsEnv) {
1244
- const services = workspace.services;
1245
- if (!services.db && !services.cache && !services.mail) return;
1246
- const servicesToStart = [];
1247
- if (services.db) servicesToStart.push("postgres");
1248
- if (services.cache) servicesToStart.push("redis");
1249
- if (services.mail) servicesToStart.push("mailpit");
1260
+ const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
1261
+ if (!(0, node_fs.existsSync)(composeFile)) return;
1262
+ const allServices = parseComposeServiceNames(composeFile);
1263
+ const appNames = new Set(Object.keys(workspace.apps));
1264
+ const servicesToStart = allServices.filter((name$1) => !appNames.has(name$1));
1250
1265
  if (servicesToStart.length === 0) return;
1251
1266
  logger$11.log(`🐳 Starting services: ${servicesToStart.join(", ")}`);
1252
1267
  try {
1253
- const composeFile = (0, node_path.join)(workspace.root, "docker-compose.yml");
1254
- if (!(0, node_fs.existsSync)(composeFile)) {
1255
- logger$11.warn("⚠️ No docker-compose.yml found. Services will not be started.");
1256
- return;
1257
- }
1258
1268
  (0, node_child_process.execSync)(`docker compose up -d ${servicesToStart.join(" ")}`, {
1259
1269
  cwd: workspace.root,
1260
1270
  stdio: "inherit",
@@ -2188,7 +2198,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
2188
2198
  let masterKey;
2189
2199
  if (context.production?.bundle && !skipBundle) {
2190
2200
  logger$9.log(`\n📦 Bundling production server...`);
2191
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-NpfYPBUo.cjs"));
2201
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-Bm3Az_sv.cjs"));
2192
2202
  const allConstructs = [
2193
2203
  ...endpoints.map((e) => e.construct),
2194
2204
  ...functions.map((f) => f.construct),
@@ -2850,13 +2860,15 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
2850
2860
  const DEFAULT_SERVICE_IMAGES = {
2851
2861
  postgres: "postgres",
2852
2862
  redis: "redis",
2853
- rabbitmq: "rabbitmq"
2863
+ rabbitmq: "rabbitmq",
2864
+ minio: "minio/minio"
2854
2865
  };
2855
2866
  /** Default Docker image versions for services */
2856
2867
  const DEFAULT_SERVICE_VERSIONS = {
2857
2868
  postgres: "18-alpine",
2858
2869
  redis: "7-alpine",
2859
- rabbitmq: "3-management-alpine"
2870
+ rabbitmq: "3-management-alpine",
2871
+ minio: "latest"
2860
2872
  };
2861
2873
  /** Get the default full image reference for a service */
2862
2874
  function getDefaultImage(serviceName) {
@@ -2909,6 +2921,13 @@ services:
2909
2921
  if (serviceMap.has("redis")) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
2910
2922
  `;
2911
2923
  if (serviceMap.has("rabbitmq")) yaml$1 += ` - RABBITMQ_URL=\${RABBITMQ_URL:-amqp://rabbitmq:5672}
2924
+ `;
2925
+ if (serviceMap.has("minio")) yaml$1 += ` - S3_ENDPOINT=\${S3_ENDPOINT:-http://minio:9000}
2926
+ - S3_ACCESS_KEY_ID=\${MINIO_ACCESS_KEY:-app}
2927
+ - S3_SECRET_ACCESS_KEY=\${MINIO_SECRET_KEY:-app}
2928
+ - S3_BUCKET=\${MINIO_BUCKET:-app}
2929
+ - S3_REGION=\${S3_REGION:-eu-west-1}
2930
+ - S3_FORCE_PATH_STYLE=true
2912
2931
  `;
2913
2932
  yaml$1 += ` healthcheck:
2914
2933
  test: ["CMD", "wget", "-q", "--spider", "http://localhost:${port}${healthCheckPath}"]
@@ -2982,6 +3001,29 @@ services:
2982
3001
  retries: 5
2983
3002
  networks:
2984
3003
  - app-network
3004
+ `;
3005
+ const minioImage = serviceMap.get("minio");
3006
+ if (minioImage) yaml$1 += `
3007
+ minio:
3008
+ image: ${minioImage}
3009
+ container_name: minio
3010
+ restart: unless-stopped
3011
+ entrypoint: sh
3012
+ command: -c 'mkdir -p /data/\${MINIO_BUCKET:-app} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
3013
+ environment:
3014
+ MINIO_ROOT_USER: \${MINIO_ACCESS_KEY:-app}
3015
+ MINIO_ROOT_PASSWORD: \${MINIO_SECRET_KEY:-app}
3016
+ ports:
3017
+ - "9001:9001" # Console UI
3018
+ volumes:
3019
+ - minio_data:/data
3020
+ healthcheck:
3021
+ test: ["CMD", "mc", "ready", "local"]
3022
+ interval: 10s
3023
+ timeout: 5s
3024
+ retries: 5
3025
+ networks:
3026
+ - app-network
2985
3027
  `;
2986
3028
  yaml$1 += `
2987
3029
  volumes:
@@ -2991,6 +3033,8 @@ volumes:
2991
3033
  if (serviceMap.has("redis")) yaml$1 += ` redis_data:
2992
3034
  `;
2993
3035
  if (serviceMap.has("rabbitmq")) yaml$1 += ` rabbitmq_data:
3036
+ `;
3037
+ if (serviceMap.has("minio")) yaml$1 += ` minio_data:
2994
3038
  `;
2995
3039
  yaml$1 += `
2996
3040
  networks:
@@ -3046,8 +3090,10 @@ function generateWorkspaceCompose(workspace, options = {}) {
3046
3090
  const hasPostgres = services.db !== void 0 && services.db !== false;
3047
3091
  const hasRedis = services.cache !== void 0 && services.cache !== false;
3048
3092
  const hasMail = services.mail !== void 0 && services.mail !== false;
3093
+ const hasMinio = services.storage !== void 0 && services.storage !== false;
3049
3094
  const postgresImage = getInfraServiceImage("postgres", services.db);
3050
3095
  const redisImage = getInfraServiceImage("redis", services.cache);
3096
+ const minioImage = getInfraServiceImage("minio", services.storage);
3051
3097
  let yaml$1 = `# Docker Compose for ${workspace.name} workspace
3052
3098
  # Use "gkm dev" or "gkm test" to start services.
3053
3099
  # Running "docker compose up" directly will not inject secrets or resolve ports.
@@ -3057,7 +3103,8 @@ services:
3057
3103
  for (const [appName, app] of apps) yaml$1 += generateAppService(appName, app, apps, {
3058
3104
  registry,
3059
3105
  hasPostgres,
3060
- hasRedis
3106
+ hasRedis,
3107
+ hasMinio
3061
3108
  });
3062
3109
  if (hasPostgres) yaml$1 += `
3063
3110
  postgres:
@@ -3103,6 +3150,28 @@ services:
3103
3150
  - "1025:1025" # SMTP
3104
3151
  networks:
3105
3152
  - workspace-network
3153
+ `;
3154
+ if (hasMinio) yaml$1 += `
3155
+ minio:
3156
+ image: ${minioImage}
3157
+ container_name: ${workspace.name}-minio
3158
+ restart: unless-stopped
3159
+ entrypoint: sh
3160
+ command: -c 'mkdir -p /data/\${MINIO_BUCKET:-app} && /usr/bin/docker-entrypoint.sh server --console-address ":9001" /data'
3161
+ environment:
3162
+ MINIO_ROOT_USER: \${MINIO_ACCESS_KEY:-app}
3163
+ MINIO_ROOT_PASSWORD: \${MINIO_SECRET_KEY:-app}
3164
+ ports:
3165
+ - "9001:9001" # Console UI
3166
+ volumes:
3167
+ - minio_data:/data
3168
+ healthcheck:
3169
+ test: ["CMD", "mc", "ready", "local"]
3170
+ interval: 10s
3171
+ timeout: 5s
3172
+ retries: 5
3173
+ networks:
3174
+ - workspace-network
3106
3175
  `;
3107
3176
  yaml$1 += `
3108
3177
  volumes:
@@ -3110,6 +3179,8 @@ volumes:
3110
3179
  if (hasPostgres) yaml$1 += ` dbdata:
3111
3180
  `;
3112
3181
  if (hasRedis) yaml$1 += ` redis_data:
3182
+ `;
3183
+ if (hasMinio) yaml$1 += ` minio_data:
3113
3184
  `;
3114
3185
  yaml$1 += `
3115
3186
  networks:
@@ -3124,14 +3195,19 @@ networks:
3124
3195
  function getInfraServiceImage(serviceName, config) {
3125
3196
  const defaults = {
3126
3197
  postgres: "postgres:18-alpine",
3127
- redis: "redis:7-alpine"
3198
+ redis: "redis:7-alpine",
3199
+ minio: "minio/minio:latest"
3128
3200
  };
3129
3201
  if (!config || config === true) return defaults[serviceName];
3130
3202
  if (typeof config === "object") {
3131
3203
  if (config.image) return config.image;
3132
3204
  if (config.version) {
3133
- const baseImage = serviceName === "postgres" ? "postgres" : "redis";
3134
- return `${baseImage}:${config.version}`;
3205
+ const baseImages = {
3206
+ postgres: "postgres",
3207
+ redis: "redis",
3208
+ minio: "minio/minio"
3209
+ };
3210
+ return `${baseImages[serviceName]}:${config.version}`;
3135
3211
  }
3136
3212
  }
3137
3213
  return defaults[serviceName];
@@ -3140,7 +3216,7 @@ function getInfraServiceImage(serviceName, config) {
3140
3216
  * Generate a service definition for an app.
3141
3217
  */
3142
3218
  function generateAppService(appName, app, allApps, options) {
3143
- const { registry, hasPostgres, hasRedis } = options;
3219
+ const { registry, hasPostgres, hasRedis, hasMinio } = options;
3144
3220
  const imageRef = registry ? `\${REGISTRY:-${registry}}/` : "";
3145
3221
  const healthCheckPath = app.type === "frontend" ? "/" : "/health";
3146
3222
  const healthCheckCmd = app.type === "frontend" ? `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}/"]` : `["CMD", "wget", "-q", "--spider", "http://localhost:${app.port}${healthCheckPath}"]`;
@@ -3167,6 +3243,13 @@ function generateAppService(appName, app, allApps, options) {
3167
3243
  if (hasPostgres) yaml$1 += ` - DATABASE_URL=\${DATABASE_URL:-postgresql://\${POSTGRES_USER:-postgres}:\${POSTGRES_PASSWORD:-postgres}@postgres:5432/\${POSTGRES_DB:-app}}
3168
3244
  `;
3169
3245
  if (hasRedis) yaml$1 += ` - REDIS_URL=\${REDIS_URL:-redis://redis:6379}
3246
+ `;
3247
+ if (hasMinio) yaml$1 += ` - S3_ENDPOINT=\${S3_ENDPOINT:-http://minio:9000}
3248
+ - S3_ACCESS_KEY_ID=\${MINIO_ACCESS_KEY:-app}
3249
+ - S3_SECRET_ACCESS_KEY=\${MINIO_SECRET_KEY:-app}
3250
+ - S3_BUCKET=\${MINIO_BUCKET:-app}
3251
+ - S3_REGION=\${S3_REGION:-eu-west-1}
3252
+ - S3_FORCE_PATH_STYLE=true
3170
3253
  `;
3171
3254
  }
3172
3255
  yaml$1 += ` healthcheck:
@@ -3179,6 +3262,7 @@ function generateAppService(appName, app, allApps, options) {
3179
3262
  if (app.type === "backend") {
3180
3263
  if (hasPostgres) dependencies$1.push("postgres");
3181
3264
  if (hasRedis) dependencies$1.push("redis");
3265
+ if (hasMinio) dependencies$1.push("minio");
3182
3266
  }
3183
3267
  if (dependencies$1.length > 0) {
3184
3268
  yaml$1 += ` depends_on:
@@ -6223,7 +6307,7 @@ async function deployCommand(options) {
6223
6307
  dokployConfig = setupResult.config;
6224
6308
  finalRegistry = dokployConfig.registry ?? dockerConfig.registry;
6225
6309
  if (setupResult.serviceUrls) {
6226
- const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
6310
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1, initStageSecrets } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
6227
6311
  let secrets = await readStageSecrets$1(stage);
6228
6312
  if (!secrets) {
6229
6313
  logger$3.log(` Creating secrets file for stage "${stage}"...`);
@@ -6520,7 +6604,7 @@ const GEEKMIDAS_VERSIONS = {
6520
6604
  "@geekmidas/storage": "~2.0.0",
6521
6605
  "@geekmidas/studio": "~1.0.0",
6522
6606
  "@geekmidas/telescope": "~1.0.0",
6523
- "@geekmidas/testkit": "~1.0.2",
6607
+ "@geekmidas/testkit": "~1.0.5",
6524
6608
  "@geekmidas/cli": CLI_VERSION
6525
6609
  };
6526
6610
 
@@ -10888,6 +10972,7 @@ async function initCommand(projectName, options = {}) {
10888
10972
  const secretServices = [];
10889
10973
  if (services.db) secretServices.push("postgres");
10890
10974
  if (services.cache) secretServices.push("redis");
10975
+ if (services.storage) secretServices.push("minio");
10891
10976
  const devSecrets = require_fullstack_secrets.createStageSecrets("development", secretServices, { projectName: name$1 });
10892
10977
  const customSecrets = {
10893
10978
  NODE_ENV: "development",
@@ -11046,6 +11131,7 @@ async function secretsInitCommand(options) {
11046
11131
  if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
11047
11132
  if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
11048
11133
  if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
11134
+ if (secrets.urls.S3_ENDPOINT) logger$2.log(` S3_ENDPOINT: ${secrets.urls.S3_ENDPOINT}`);
11049
11135
  if (Object.keys(secrets.custom).length > 0) logger$2.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
11050
11136
  logger$2.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
11051
11137
  logger$2.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
@@ -11107,11 +11193,13 @@ async function secretsShowCommand(options) {
11107
11193
  logger$2.log(` password: ${reveal ? creds.password : require_storage.maskPassword(creds.password)}`);
11108
11194
  if (creds.database) logger$2.log(` database: ${creds.database}`);
11109
11195
  if (creds.vhost) logger$2.log(` vhost: ${creds.vhost}`);
11196
+ if (creds.bucket) logger$2.log(` bucket: ${creds.bucket}`);
11110
11197
  }
11111
11198
  logger$2.log("\nConnection URLs:");
11112
11199
  if (secrets.urls.DATABASE_URL) logger$2.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
11113
11200
  if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
11114
11201
  if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
11202
+ if (secrets.urls.S3_ENDPOINT) logger$2.log(` S3_ENDPOINT: ${secrets.urls.S3_ENDPOINT}`);
11115
11203
  const customKeys = Object.keys(secrets.custom);
11116
11204
  if (customKeys.length > 0) {
11117
11205
  logger$2.log("\nCustom Secrets:");
@@ -11292,20 +11380,56 @@ async function resolveSecrets(stage, workspace, options) {
11292
11380
  * @internal Exported for testing
11293
11381
  */
11294
11382
  function reconcileSecrets(secrets, workspace) {
11383
+ let changed = false;
11384
+ let result = { ...secrets };
11385
+ const serviceMap = [
11386
+ {
11387
+ key: "db",
11388
+ name: "postgres"
11389
+ },
11390
+ {
11391
+ key: "cache",
11392
+ name: "redis"
11393
+ },
11394
+ {
11395
+ key: "storage",
11396
+ name: "minio"
11397
+ }
11398
+ ];
11399
+ for (const { key, name: name$1 } of serviceMap) if (workspace.services[key] && !result.services[name$1]) {
11400
+ const creds = require_fullstack_secrets.generateServiceCredentials(name$1);
11401
+ result = {
11402
+ ...result,
11403
+ services: {
11404
+ ...result.services,
11405
+ [name$1]: creds
11406
+ }
11407
+ };
11408
+ result.urls = require_fullstack_secrets.generateConnectionUrls(result.services);
11409
+ logger$1.log(` 🔄 Adding missing service credentials: ${name$1}`);
11410
+ changed = true;
11411
+ }
11295
11412
  const isMultiApp = Object.keys(workspace.apps).length > 1;
11296
- if (!isMultiApp) return null;
11297
- const expected = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
11298
- const missing = {};
11299
- for (const [key, value] of Object.entries(expected)) if (!(key in secrets.custom)) missing[key] = value;
11300
- if (Object.keys(missing).length === 0) return null;
11301
- logger$1.log(` 🔄 Adding missing secrets: ${Object.keys(missing).join(", ")}`);
11302
- return {
11303
- ...secrets,
11304
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
11305
- custom: {
11306
- ...secrets.custom,
11307
- ...missing
11413
+ if (isMultiApp) {
11414
+ const expected = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
11415
+ const missing = {};
11416
+ for (const [key, value] of Object.entries(expected)) if (!(key in result.custom)) missing[key] = value;
11417
+ if (Object.keys(missing).length > 0) {
11418
+ logger$1.log(` 🔄 Adding missing secrets: ${Object.keys(missing).join(", ")}`);
11419
+ result = {
11420
+ ...result,
11421
+ custom: {
11422
+ ...result.custom,
11423
+ ...missing
11424
+ }
11425
+ };
11426
+ changed = true;
11308
11427
  }
11428
+ }
11429
+ if (!changed) return null;
11430
+ return {
11431
+ ...result,
11432
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
11309
11433
  };
11310
11434
  }
11311
11435
  /**
@@ -11315,6 +11439,7 @@ async function generateFreshSecrets(stage, workspace, options) {
11315
11439
  const serviceNames = [];
11316
11440
  if (workspace.services.db) serviceNames.push("postgres");
11317
11441
  if (workspace.services.cache) serviceNames.push("redis");
11442
+ if (workspace.services.storage) serviceNames.push("minio");
11318
11443
  const secrets = require_fullstack_secrets.createStageSecrets(stage, serviceNames, { projectName: workspace.name });
11319
11444
  const isMultiApp = Object.keys(workspace.apps).length > 1;
11320
11445
  if (isMultiApp) {
@@ -11858,9 +11983,9 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
11858
11983
  const globalOptions = program.opts();
11859
11984
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11860
11985
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11861
- const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
11862
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-TEBsryVn.cjs"));
11863
- const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
11986
+ const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-DLlwsrBs.cjs"));
11987
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BZ8j_-0z.cjs"));
11988
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
11864
11989
  const { workspace } = await loadWorkspaceConfig$1();
11865
11990
  const secrets = await readStageSecrets$1(options.stage, workspace.root);
11866
11991
  if (secrets) {
@@ -11883,9 +12008,9 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
11883
12008
  const globalOptions = program.opts();
11884
12009
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11885
12010
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11886
- const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
11887
- const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
11888
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-TEBsryVn.cjs"));
12011
+ const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-DLlwsrBs.cjs"));
12012
+ const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
12013
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BZ8j_-0z.cjs"));
11889
12014
  const { workspace } = await loadWorkspaceConfig$1();
11890
12015
  let secrets = await pullSecrets$1(options.stage, workspace);
11891
12016
  if (!secrets) {
@@ -11910,8 +12035,8 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
11910
12035
  const globalOptions = program.opts();
11911
12036
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11912
12037
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11913
- const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-TEBsryVn.cjs"));
11914
- const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
12038
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-BZ8j_-0z.cjs"));
12039
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-D6BGLgWf.cjs"));
11915
12040
  const { workspace } = await loadWorkspaceConfig$1();
11916
12041
  const secrets = await readStageSecrets$1(options.stage, workspace.root);
11917
12042
  if (!secrets) {