@geekmidas/cli 1.9.0 → 1.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/{HostingerProvider-CEsQbmpY.cjs → HostingerProvider-5KYmwoK2.cjs} +1 -1
  3. package/dist/{HostingerProvider-CEsQbmpY.cjs.map → HostingerProvider-5KYmwoK2.cjs.map} +1 -1
  4. package/dist/{HostingerProvider-DkahM5AP.mjs → HostingerProvider-ANWchdiK.mjs} +1 -1
  5. package/dist/{HostingerProvider-DkahM5AP.mjs.map → HostingerProvider-ANWchdiK.mjs.map} +1 -1
  6. package/dist/{LocalStateProvider-Roi202l7.cjs → LocalStateProvider-CLifRC0Y.cjs} +1 -1
  7. package/dist/{LocalStateProvider-Roi202l7.cjs.map → LocalStateProvider-CLifRC0Y.cjs.map} +1 -1
  8. package/dist/{LocalStateProvider-DXIwWb7k.mjs → LocalStateProvider-Dp0KkRcw.mjs} +1 -1
  9. package/dist/{LocalStateProvider-DXIwWb7k.mjs.map → LocalStateProvider-Dp0KkRcw.mjs.map} +1 -1
  10. package/dist/{Route53Provider-Ckq_n5Be.mjs → Route53Provider-QoPgcXxn.mjs} +1 -1
  11. package/dist/{Route53Provider-Ckq_n5Be.mjs.map → Route53Provider-QoPgcXxn.mjs.map} +1 -1
  12. package/dist/{Route53Provider-BqXeHzuc.cjs → Route53Provider-owQQ4pn6.cjs} +1 -1
  13. package/dist/{Route53Provider-BqXeHzuc.cjs.map → Route53Provider-owQQ4pn6.cjs.map} +1 -1
  14. package/dist/{SSMStateProvider-BReQA5re.cjs → SSMStateProvider-CT8tjl9o.cjs} +1 -1
  15. package/dist/{SSMStateProvider-BReQA5re.cjs.map → SSMStateProvider-CT8tjl9o.cjs.map} +1 -1
  16. package/dist/{SSMStateProvider-wddd0_-d.mjs → SSMStateProvider-CksOTB8M.mjs} +1 -1
  17. package/dist/{SSMStateProvider-wddd0_-d.mjs.map → SSMStateProvider-CksOTB8M.mjs.map} +1 -1
  18. package/dist/{backup-provisioner-BAExdDtc.mjs → backup-provisioner-BEXoHTuC.mjs} +1 -1
  19. package/dist/{backup-provisioner-BAExdDtc.mjs.map → backup-provisioner-BEXoHTuC.mjs.map} +1 -1
  20. package/dist/{backup-provisioner-C8VK63I-.cjs → backup-provisioner-C4noe75O.cjs} +1 -1
  21. package/dist/{backup-provisioner-C8VK63I-.cjs.map → backup-provisioner-C4noe75O.cjs.map} +1 -1
  22. package/dist/{bundler-BxHyDhdt.mjs → bundler-DQYjKFPm.mjs} +1 -1
  23. package/dist/{bundler-BxHyDhdt.mjs.map → bundler-DQYjKFPm.mjs.map} +1 -1
  24. package/dist/{bundler-CuMIfXw5.cjs → bundler-NpfYPBUo.cjs} +1 -1
  25. package/dist/{bundler-CuMIfXw5.cjs.map → bundler-NpfYPBUo.cjs.map} +1 -1
  26. package/dist/config.d.mts +2 -2
  27. package/dist/fullstack-secrets-COWz084x.cjs +238 -0
  28. package/dist/fullstack-secrets-COWz084x.cjs.map +1 -0
  29. package/dist/fullstack-secrets-UZAFWuH4.mjs +202 -0
  30. package/dist/fullstack-secrets-UZAFWuH4.mjs.map +1 -0
  31. package/dist/{index-BVNXOydm.d.mts → index-Bt2kX0-R.d.mts} +2 -2
  32. package/dist/{index-BVNXOydm.d.mts.map → index-Bt2kX0-R.d.mts.map} +1 -1
  33. package/dist/index.cjs +141 -276
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.mjs +128 -263
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/{openapi-react-query-DaTMSPD5.mjs → openapi-react-query-C4UdILaI.mjs} +1 -1
  38. package/dist/{openapi-react-query-DaTMSPD5.mjs.map → openapi-react-query-C4UdILaI.mjs.map} +1 -1
  39. package/dist/{openapi-react-query-BeXvk-wa.cjs → openapi-react-query-DYbBq-WJ.cjs} +1 -1
  40. package/dist/{openapi-react-query-BeXvk-wa.cjs.map → openapi-react-query-DYbBq-WJ.cjs.map} +1 -1
  41. package/dist/openapi-react-query.cjs +1 -1
  42. package/dist/openapi-react-query.mjs +1 -1
  43. package/dist/openapi.d.mts +1 -1
  44. package/dist/reconcile-7yarEvmK.cjs +36 -0
  45. package/dist/reconcile-7yarEvmK.cjs.map +1 -0
  46. package/dist/reconcile-D2WCDQue.mjs +36 -0
  47. package/dist/reconcile-D2WCDQue.mjs.map +1 -0
  48. package/dist/{sync-BnqNNc6O.mjs → sync-6FoT41G3.mjs} +1 -1
  49. package/dist/{sync-CHfhmXF3.mjs → sync-CbeKrnQV.mjs} +1 -1
  50. package/dist/{sync-CHfhmXF3.mjs.map → sync-CbeKrnQV.mjs.map} +1 -1
  51. package/dist/{sync-BOS0jKLn.cjs → sync-DdkKaHqP.cjs} +1 -1
  52. package/dist/{sync-BOS0jKLn.cjs.map → sync-DdkKaHqP.cjs.map} +1 -1
  53. package/dist/sync-RsnjXYwG.cjs +4 -0
  54. package/dist/{types-eTlj5f2M.d.mts → types-wXMIMOyK.d.mts} +1 -1
  55. package/dist/{types-eTlj5f2M.d.mts.map → types-wXMIMOyK.d.mts.map} +1 -1
  56. package/dist/workspace/index.d.mts +2 -2
  57. package/package.json +3 -3
  58. package/src/dev/__tests__/index.spec.ts +49 -0
  59. package/src/dev/index.ts +84 -63
  60. package/src/index.ts +79 -1
  61. package/src/init/versions.ts +4 -4
  62. package/src/secrets/__tests__/reconcile.spec.ts +123 -0
  63. package/src/secrets/reconcile.ts +53 -0
  64. package/src/setup/fullstack-secrets.ts +2 -0
  65. package/dist/sync-BxFB34zW.cjs +0 -4
package/dist/index.cjs CHANGED
@@ -8,8 +8,9 @@ const require_storage = require('./storage-CoCNe0Pt.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_openapi_react_query = require('./openapi-react-query-BeXvk-wa.cjs');
12
- const require_sync = require('./sync-BOS0jKLn.cjs');
11
+ const require_fullstack_secrets = require('./fullstack-secrets-COWz084x.cjs');
12
+ const require_openapi_react_query = require('./openapi-react-query-DYbBq-WJ.cjs');
13
+ const require_sync = require('./sync-DdkKaHqP.cjs');
13
14
  const node_fs = require_chunk.__toESM(require("node:fs"));
14
15
  const node_path = require_chunk.__toESM(require("node:path"));
15
16
  const commander = require_chunk.__toESM(require("commander"));
@@ -34,7 +35,7 @@ const prompts = require_chunk.__toESM(require("prompts"));
34
35
 
35
36
  //#region package.json
36
37
  var name = "@geekmidas/cli";
37
- var version = "1.8.0";
38
+ var version = "1.9.0";
38
39
  var description = "CLI tools for building Lambda handlers, server applications, and generating OpenAPI specs";
39
40
  var private$1 = false;
40
41
  var type = "module";
@@ -1740,6 +1741,66 @@ var EntryRunner = class {
1740
1741
  }
1741
1742
  }
1742
1743
  };
1744
+ /**
1745
+ * Generate the content of the dev server entry file (server.ts).
1746
+ * Uses dynamic import for createApp so Credentials are populated
1747
+ * before any app modules evaluate.
1748
+ * @internal Exported for testing
1749
+ */
1750
+ function generateServerEntryContent(options) {
1751
+ const { secretsJsonPath, runtime = "node", enableOpenApi = false, appImportPath = "./app.js" } = options;
1752
+ const credentialsInjection = secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
1753
+ import { existsSync, readFileSync } from 'node:fs';
1754
+
1755
+ // Inject dev secrets into Credentials (must happen before app import)
1756
+ const secretsPath = '${secretsJsonPath}';
1757
+ if (existsSync(secretsPath)) {
1758
+ Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
1759
+ }
1760
+
1761
+ ` : "";
1762
+ const serveCode = runtime === "bun" ? `Bun.serve({
1763
+ port,
1764
+ fetch: app.fetch,
1765
+ });` : `const { serve } = await import('@hono/node-server');
1766
+ const server = serve({
1767
+ fetch: app.fetch,
1768
+ port,
1769
+ });
1770
+ // Inject WebSocket support if available
1771
+ const injectWs = (app as any).__injectWebSocket;
1772
+ if (injectWs) {
1773
+ injectWs(server);
1774
+ console.log('🔌 Telescope real-time updates enabled');
1775
+ }`;
1776
+ return `#!/usr/bin/env node
1777
+ /**
1778
+ * Development server entry point
1779
+ * This file is auto-generated by 'gkm dev'
1780
+ */
1781
+ ${credentialsInjection}
1782
+ const port = process.argv.includes('--port')
1783
+ ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1784
+ : 3000;
1785
+
1786
+ // Dynamic import so Credentials are populated before env.ts evaluates
1787
+ const { createApp } = await import('${appImportPath}');
1788
+
1789
+ // createApp is async to support optional WebSocket setup
1790
+ const { app, start } = await createApp(undefined, ${enableOpenApi});
1791
+
1792
+ // Start the server
1793
+ start({
1794
+ port,
1795
+ serve: async (app, port) => {
1796
+ ${serveCode}
1797
+ },
1798
+ }).catch((error) => {
1799
+ console.error('Failed to start server:', error);
1800
+ process.exit(1);
1801
+ });
1802
+ `;
1803
+ }
1743
1804
  var DevServer = class {
1744
1805
  serverProcess = null;
1745
1806
  isRunning = false;
@@ -1833,58 +1894,12 @@ var DevServer = class {
1833
1894
  }
1834
1895
  async createServerEntry() {
1835
1896
  const { writeFile: fsWriteFile } = await import("node:fs/promises");
1836
- const { relative: relative$6, dirname: dirname$11 } = await import("node:path");
1837
1897
  const serverPath = (0, node_path.join)(this.appRoot, ".gkm", this.provider, "server.ts");
1838
- const relativeAppPath = relative$6(dirname$11(serverPath), (0, node_path.join)(dirname$11(serverPath), "app.js"));
1839
- const credentialsInjection = this.secretsJsonPath ? `import { Credentials } from '@geekmidas/envkit/credentials';
1840
- import { existsSync, readFileSync } from 'node:fs';
1841
-
1842
- // Inject dev secrets into Credentials (must happen before app import)
1843
- const secretsPath = '${this.secretsJsonPath}';
1844
- if (existsSync(secretsPath)) {
1845
- Object.assign(Credentials, JSON.parse(readFileSync(secretsPath, 'utf-8')));
1846
- }
1847
-
1848
- ` : "";
1849
- const serveCode = this.runtime === "bun" ? `Bun.serve({
1850
- port,
1851
- fetch: app.fetch,
1852
- });` : `const { serve } = await import('@hono/node-server');
1853
- const server = serve({
1854
- fetch: app.fetch,
1855
- port,
1856
- });
1857
- // Inject WebSocket support if available
1858
- const injectWs = (app as any).__injectWebSocket;
1859
- if (injectWs) {
1860
- injectWs(server);
1861
- console.log('🔌 Telescope real-time updates enabled');
1862
- }`;
1863
- const content = `#!/usr/bin/env node
1864
- /**
1865
- * Development server entry point
1866
- * This file is auto-generated by 'gkm dev'
1867
- */
1868
- ${credentialsInjection}import { createApp } from './${relativeAppPath.startsWith(".") ? relativeAppPath : `./${relativeAppPath}`}';
1869
-
1870
- const port = process.argv.includes('--port')
1871
- ? Number.parseInt(process.argv[process.argv.indexOf('--port') + 1])
1872
- : 3000;
1873
-
1874
- // createApp is async to support optional WebSocket setup
1875
- const { app, start } = await createApp(undefined, ${this.enableOpenApi});
1876
-
1877
- // Start the server
1878
- start({
1879
- port,
1880
- serve: async (app, port) => {
1881
- ${serveCode}
1882
- },
1883
- }).catch((error) => {
1884
- console.error('Failed to start server:', error);
1885
- process.exit(1);
1886
- });
1887
- `;
1898
+ const content = generateServerEntryContent({
1899
+ secretsJsonPath: this.secretsJsonPath,
1900
+ runtime: this.runtime,
1901
+ enableOpenApi: this.enableOpenApi
1902
+ });
1888
1903
  await fsWriteFile(serverPath, content);
1889
1904
  }
1890
1905
  };
@@ -2137,7 +2152,7 @@ async function buildForProvider(provider, context, rootOutputDir, endpointGenera
2137
2152
  let masterKey;
2138
2153
  if (context.production?.bundle && !skipBundle) {
2139
2154
  logger$9.log(`\n📦 Bundling production server...`);
2140
- const { bundleServer } = await Promise.resolve().then(() => require("./bundler-CuMIfXw5.cjs"));
2155
+ const { bundleServer } = await Promise.resolve().then(() => require("./bundler-NpfYPBUo.cjs"));
2141
2156
  const allConstructs = [
2142
2157
  ...endpoints.map((e) => e.construct),
2143
2158
  ...functions.map((f) => f.construct),
@@ -2416,11 +2431,11 @@ async function createDnsProvider(options) {
2416
2431
  if (isDnsProvider(config.provider)) return config.provider;
2417
2432
  const provider = config.provider;
2418
2433
  if (provider === "hostinger") {
2419
- const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-CEsQbmpY.cjs"));
2434
+ const { HostingerProvider } = await Promise.resolve().then(() => require("./HostingerProvider-5KYmwoK2.cjs"));
2420
2435
  return new HostingerProvider();
2421
2436
  }
2422
2437
  if (provider === "route53") {
2423
- const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-BqXeHzuc.cjs"));
2438
+ const { Route53Provider } = await Promise.resolve().then(() => require("./Route53Provider-owQQ4pn6.cjs"));
2424
2439
  const route53Config = config;
2425
2440
  return new Route53Provider({
2426
2441
  region: route53Config.region,
@@ -4668,19 +4683,19 @@ function isStateProvider(value) {
4668
4683
  async function createStateProvider(options) {
4669
4684
  const { config, workspaceRoot, workspaceName } = options;
4670
4685
  if (!config) {
4671
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
4686
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CLifRC0Y.cjs"));
4672
4687
  return new LocalStateProvider(workspaceRoot);
4673
4688
  }
4674
4689
  if (isStateProvider(config.provider)) return config.provider;
4675
4690
  const provider = config.provider;
4676
4691
  if (provider === "local") {
4677
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
4692
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CLifRC0Y.cjs"));
4678
4693
  return new LocalStateProvider(workspaceRoot);
4679
4694
  }
4680
4695
  if (provider === "ssm") {
4681
4696
  if (!workspaceName) throw new Error("Workspace name is required for SSM state provider. Set \"name\" in gkm.config.ts.");
4682
- const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-Roi202l7.cjs"));
4683
- const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-BReQA5re.cjs"));
4697
+ const { LocalStateProvider } = await Promise.resolve().then(() => require("./LocalStateProvider-CLifRC0Y.cjs"));
4698
+ const { SSMStateProvider } = await Promise.resolve().then(() => require("./SSMStateProvider-CT8tjl9o.cjs"));
4684
4699
  const { CachedStateProvider: CachedStateProvider$1 } = await Promise.resolve().then(() => require("./CachedStateProvider-D_uISMmJ.cjs"));
4685
4700
  const ssmConfig = config;
4686
4701
  const local = new LocalStateProvider(workspaceRoot);
@@ -5355,8 +5370,8 @@ async function provisionServices(api, projectId, environmentId, projectName, ser
5355
5370
  else logger$3.log(` ⚠ Cached ID invalid, will create new`);
5356
5371
  }
5357
5372
  if (!redis) {
5358
- const { randomBytes: randomBytes$3 } = await import("node:crypto");
5359
- const databasePassword = randomBytes$3(16).toString("hex");
5373
+ const { randomBytes: randomBytes$2 } = await import("node:crypto");
5374
+ const databasePassword = randomBytes$2(16).toString("hex");
5360
5375
  const result = await api.findOrCreateRedis(redisName, projectId, environmentId, { databasePassword });
5361
5376
  redis = result.redis;
5362
5377
  created = result.created;
@@ -5780,7 +5795,7 @@ async function workspaceDeployCommand(workspace, options) {
5780
5795
  }
5781
5796
  if (workspace.deploy?.backups && provisionedPostgres) {
5782
5797
  logger$3.log("\n💾 Provisioning backup destination...");
5783
- const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-C8VK63I-.cjs"));
5798
+ const { provisionBackupDestination } = await Promise.resolve().then(() => require("./backup-provisioner-C4noe75O.cjs"));
5784
5799
  const backupState = await provisionBackupDestination({
5785
5800
  api,
5786
5801
  projectId: project.projectId,
@@ -6405,200 +6420,6 @@ function printStateDetails(state) {
6405
6420
  }
6406
6421
  }
6407
6422
 
6408
- //#endregion
6409
- //#region src/secrets/generator.ts
6410
- /**
6411
- * Generate a secure random password using URL-safe base64 characters.
6412
- * @param length Password length (default: 32)
6413
- */
6414
- function generateSecurePassword(length = 32) {
6415
- return (0, node_crypto.randomBytes)(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
6416
- }
6417
- /** Default service configurations */
6418
- const SERVICE_DEFAULTS = {
6419
- postgres: {
6420
- host: "postgres",
6421
- port: 5432,
6422
- username: "app",
6423
- database: "app"
6424
- },
6425
- redis: {
6426
- host: "redis",
6427
- port: 6379,
6428
- username: "default"
6429
- },
6430
- rabbitmq: {
6431
- host: "rabbitmq",
6432
- port: 5672,
6433
- username: "app",
6434
- vhost: "/"
6435
- }
6436
- };
6437
- /**
6438
- * Generate credentials for a specific service.
6439
- */
6440
- function generateServiceCredentials(service) {
6441
- const defaults = SERVICE_DEFAULTS[service];
6442
- return {
6443
- ...defaults,
6444
- password: generateSecurePassword()
6445
- };
6446
- }
6447
- /**
6448
- * Generate credentials for multiple services.
6449
- */
6450
- function generateServicesCredentials(services) {
6451
- const result = {};
6452
- for (const service of services) result[service] = generateServiceCredentials(service);
6453
- return result;
6454
- }
6455
- /**
6456
- * Generate connection URL for PostgreSQL.
6457
- */
6458
- function generatePostgresUrl(creds) {
6459
- const { username, password, host, port, database } = creds;
6460
- return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
6461
- }
6462
- /**
6463
- * Generate connection URL for Redis.
6464
- */
6465
- function generateRedisUrl(creds) {
6466
- const { password, host, port } = creds;
6467
- return `redis://:${encodeURIComponent(password)}@${host}:${port}`;
6468
- }
6469
- /**
6470
- * Generate connection URL for RabbitMQ.
6471
- */
6472
- function generateRabbitmqUrl(creds) {
6473
- const { username, password, host, port, vhost } = creds;
6474
- const encodedVhost = encodeURIComponent(vhost ?? "/");
6475
- return `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;
6476
- }
6477
- /**
6478
- * Generate connection URLs from service credentials.
6479
- */
6480
- function generateConnectionUrls(services) {
6481
- const urls = {};
6482
- if (services.postgres) urls.DATABASE_URL = generatePostgresUrl(services.postgres);
6483
- if (services.redis) urls.REDIS_URL = generateRedisUrl(services.redis);
6484
- if (services.rabbitmq) urls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);
6485
- return urls;
6486
- }
6487
- /**
6488
- * Create a new StageSecrets object with generated credentials.
6489
- */
6490
- function createStageSecrets(stage, services) {
6491
- const now = (/* @__PURE__ */ new Date()).toISOString();
6492
- const serviceCredentials = generateServicesCredentials(services);
6493
- const urls = generateConnectionUrls(serviceCredentials);
6494
- return {
6495
- stage,
6496
- createdAt: now,
6497
- updatedAt: now,
6498
- services: serviceCredentials,
6499
- urls,
6500
- custom: {}
6501
- };
6502
- }
6503
- /**
6504
- * Rotate password for a specific service.
6505
- */
6506
- function rotateServicePassword(secrets, service) {
6507
- const currentCreds = secrets.services[service];
6508
- if (!currentCreds) throw new Error(`Service "${service}" not configured in secrets`);
6509
- const newCreds = {
6510
- ...currentCreds,
6511
- password: generateSecurePassword()
6512
- };
6513
- const newServices = {
6514
- ...secrets.services,
6515
- [service]: newCreds
6516
- };
6517
- return {
6518
- ...secrets,
6519
- updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
6520
- services: newServices,
6521
- urls: generateConnectionUrls(newServices)
6522
- };
6523
- }
6524
-
6525
- //#endregion
6526
- //#region src/setup/fullstack-secrets.ts
6527
- /**
6528
- * Generate a secure random password for database users.
6529
- * Uses a combination of timestamp and random bytes for uniqueness.
6530
- */
6531
- function generateDbPassword() {
6532
- return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
6533
- }
6534
- /**
6535
- * Generate database URL for an app.
6536
- * All apps connect to the same database, but use different users/schemas.
6537
- */
6538
- function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
6539
- const userName = appName.replace(/-/g, "_");
6540
- const dbName = `${projectName.replace(/-/g, "_")}_dev`;
6541
- return `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;
6542
- }
6543
- /**
6544
- * Generate fullstack-aware custom secrets for a workspace.
6545
- *
6546
- * Generates:
6547
- * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
6548
- * - Per-app database passwords and URLs for backend apps with db service
6549
- * - Better-auth secrets for apps using the better-auth framework
6550
- */
6551
- function generateFullstackCustomSecrets(workspace) {
6552
- const hasDb = !!workspace.services.db;
6553
- const customs = {
6554
- NODE_ENV: "development",
6555
- PORT: "3000",
6556
- LOG_LEVEL: "debug",
6557
- JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
6558
- };
6559
- if (!hasDb) return customs;
6560
- const frontendPorts = [];
6561
- for (const [appName, appConfig] of Object.entries(workspace.apps)) {
6562
- if (appConfig.type === "frontend") {
6563
- frontendPorts.push(appConfig.port);
6564
- continue;
6565
- }
6566
- const password = generateDbPassword();
6567
- const upperName = appName.toUpperCase();
6568
- customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
6569
- customs[`${upperName}_DB_PASSWORD`] = password;
6570
- if (appConfig.framework === "better-auth") {
6571
- customs.AUTH_PORT = String(appConfig.port);
6572
- customs.AUTH_URL = `http://localhost:${appConfig.port}`;
6573
- customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
6574
- customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
6575
- }
6576
- }
6577
- if (customs.BETTER_AUTH_SECRET) {
6578
- const allPorts = Object.values(workspace.apps).map((a) => a.port);
6579
- customs.BETTER_AUTH_TRUSTED_ORIGINS = allPorts.map((p) => `http://localhost:${p}`).join(",");
6580
- }
6581
- return customs;
6582
- }
6583
- /**
6584
- * Extract *_DB_PASSWORD keys from secrets and write docker/.env.
6585
- *
6586
- * The docker/.env file contains database passwords that the PostgreSQL
6587
- * init script reads to create per-app database users.
6588
- */
6589
- async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
6590
- const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
6591
- if (dbPasswordEntries.length === 0) return;
6592
- const envContent = `# Auto-generated docker environment file
6593
- # Contains database passwords for docker-compose postgres init
6594
- # This file is gitignored - do not commit to version control
6595
- ${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
6596
- `;
6597
- const envPath = (0, node_path.join)(workspaceRoot, "docker", ".env");
6598
- await (0, node_fs_promises.mkdir)((0, node_path.dirname)(envPath), { recursive: true });
6599
- await (0, node_fs_promises.writeFile)(envPath, envContent);
6600
- }
6601
-
6602
6423
  //#endregion
6603
6424
  //#region src/init/versions.ts
6604
6425
  const require$1 = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href);
@@ -6624,14 +6445,14 @@ const GEEKMIDAS_VERSIONS = {
6624
6445
  "@geekmidas/audit": "~1.0.0",
6625
6446
  "@geekmidas/auth": "~1.0.0",
6626
6447
  "@geekmidas/cache": "~1.0.0",
6627
- "@geekmidas/client": "~2.0.0",
6448
+ "@geekmidas/client": "~3.0.0",
6628
6449
  "@geekmidas/cloud": "~1.0.0",
6629
- "@geekmidas/constructs": "~1.1.1",
6450
+ "@geekmidas/constructs": "~2.0.0",
6630
6451
  "@geekmidas/db": "~1.0.0",
6631
6452
  "@geekmidas/emailkit": "~1.0.0",
6632
6453
  "@geekmidas/envkit": "~1.0.3",
6633
6454
  "@geekmidas/errors": "~1.0.0",
6634
- "@geekmidas/events": "~1.0.0",
6455
+ "@geekmidas/events": "~1.1.0",
6635
6456
  "@geekmidas/logger": "~1.0.0",
6636
6457
  "@geekmidas/rate-limit": "~1.0.0",
6637
6458
  "@geekmidas/schema": "~1.0.0",
@@ -6639,7 +6460,7 @@ const GEEKMIDAS_VERSIONS = {
6639
6460
  "@geekmidas/storage": "~1.0.0",
6640
6461
  "@geekmidas/studio": "~1.0.0",
6641
6462
  "@geekmidas/telescope": "~1.0.0",
6642
- "@geekmidas/testkit": "~1.0.1",
6463
+ "@geekmidas/testkit": "~1.0.2",
6643
6464
  "@geekmidas/cli": CLI_VERSION
6644
6465
  };
6645
6466
 
@@ -10948,10 +10769,10 @@ async function initCommand(projectName, options = {}) {
10948
10769
  const dbApps = [];
10949
10770
  if (isFullstack && services.db) dbApps.push({
10950
10771
  name: "api",
10951
- password: generateDbPassword()
10772
+ password: require_fullstack_secrets.generateDbPassword()
10952
10773
  }, {
10953
10774
  name: "auth",
10954
- password: generateDbPassword()
10775
+ password: require_fullstack_secrets.generateDbPassword()
10955
10776
  });
10956
10777
  const appFiles = baseTemplate ? [
10957
10778
  ...generatePackageJson(templateOptions, baseTemplate),
@@ -11000,7 +10821,7 @@ async function initCommand(projectName, options = {}) {
11000
10821
  const secretServices = [];
11001
10822
  if (services.db) secretServices.push("postgres");
11002
10823
  if (services.cache) secretServices.push("redis");
11003
- const devSecrets = createStageSecrets("development", secretServices);
10824
+ const devSecrets = require_fullstack_secrets.createStageSecrets("development", secretServices);
11004
10825
  const customSecrets = {
11005
10826
  NODE_ENV: "development",
11006
10827
  PORT: "3000",
@@ -11010,7 +10831,7 @@ async function initCommand(projectName, options = {}) {
11010
10831
  if (isFullstack && dbApps.length > 0) {
11011
10832
  for (const app of dbApps) {
11012
10833
  const urlKey = `${app.name.toUpperCase()}_DATABASE_URL`;
11013
- customSecrets[urlKey] = generateDbUrl(app.name, app.password, name$1);
10834
+ customSecrets[urlKey] = require_fullstack_secrets.generateDbUrl(app.name, app.password, name$1);
11014
10835
  const passwordKey = `${app.name.toUpperCase()}_DB_PASSWORD`;
11015
10836
  customSecrets[passwordKey] = app.password;
11016
10837
  }
@@ -11137,12 +10958,12 @@ async function secretsInitCommand(options) {
11137
10958
  const config = await require_config.loadConfig();
11138
10959
  const services = getServicesFromConfig(config.docker?.compose?.services);
11139
10960
  if (services.length === 0) logger$2.warn("No services configured in docker.compose.services. Creating secrets with empty services.");
11140
- const secrets = createStageSecrets(stage, services);
10961
+ const secrets = require_fullstack_secrets.createStageSecrets(stage, services);
11141
10962
  try {
11142
10963
  const loaded = await require_config.loadWorkspaceConfig();
11143
10964
  const isMultiApp = Object.keys(loaded.workspace.apps).length > 1;
11144
10965
  if (isMultiApp) {
11145
- const customSecrets = generateFullstackCustomSecrets(loaded.workspace);
10966
+ const customSecrets = require_fullstack_secrets.generateFullstackCustomSecrets(loaded.workspace);
11146
10967
  secrets.custom = customSecrets;
11147
10968
  logger$2.log(" Detected workspace mode — generating per-app secrets");
11148
10969
  }
@@ -11243,13 +11064,13 @@ async function secretsRotateCommand(options) {
11243
11064
  logger$2.error(`Service "${service}" not configured in stage "${stage}"`);
11244
11065
  process.exit(1);
11245
11066
  }
11246
- const updated = rotateServicePassword(secrets, service);
11067
+ const updated = require_fullstack_secrets.rotateServicePassword(secrets, service);
11247
11068
  await require_storage.writeStageSecrets(updated);
11248
11069
  logger$2.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
11249
11070
  } else {
11250
11071
  let updated = secrets;
11251
11072
  const services = Object.keys(secrets.services);
11252
- for (const svc of services) updated = rotateServicePassword(updated, svc);
11073
+ for (const svc of services) updated = require_fullstack_secrets.rotateServicePassword(updated, svc);
11253
11074
  await require_storage.writeStageSecrets(updated);
11254
11075
  logger$2.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
11255
11076
  }
@@ -11342,7 +11163,7 @@ async function setupCommand(options = {}) {
11342
11163
  process.exit(1);
11343
11164
  }
11344
11165
  if (isMultiApp && workspace.services.db) {
11345
- await writeDockerEnvFromSecrets(secrets, workspace.root);
11166
+ await require_fullstack_secrets.writeDockerEnvFromSecrets(secrets, workspace.root);
11346
11167
  logger$1.log("📄 Generated docker/.env with database passwords");
11347
11168
  }
11348
11169
  if (!options.skipDocker) {
@@ -11396,10 +11217,10 @@ async function generateFreshSecrets(stage, workspace, options) {
11396
11217
  const serviceNames = [];
11397
11218
  if (workspace.services.db) serviceNames.push("postgres");
11398
11219
  if (workspace.services.cache) serviceNames.push("redis");
11399
- const secrets = createStageSecrets(stage, serviceNames);
11220
+ const secrets = require_fullstack_secrets.createStageSecrets(stage, serviceNames);
11400
11221
  const isMultiApp = Object.keys(workspace.apps).length > 1;
11401
11222
  if (isMultiApp) {
11402
- const customSecrets = generateFullstackCustomSecrets(workspace);
11223
+ const customSecrets = require_fullstack_secrets.generateFullstackCustomSecrets(workspace);
11403
11224
  secrets.custom = customSecrets;
11404
11225
  } else secrets.custom = {
11405
11226
  NODE_ENV: "development",
@@ -11943,8 +11764,19 @@ program.command("secrets:push").description("Push secrets to remote provider (SS
11943
11764
  const globalOptions = program.opts();
11944
11765
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11945
11766
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11946
- const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-BxFB34zW.cjs"));
11767
+ const { pushSecrets: pushSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
11768
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-7yarEvmK.cjs"));
11769
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
11947
11770
  const { workspace } = await loadWorkspaceConfig$1();
11771
+ const secrets = await readStageSecrets$1(options.stage, workspace.root);
11772
+ if (secrets) {
11773
+ const result = reconcileMissingSecrets(secrets, workspace);
11774
+ if (result) {
11775
+ await writeStageSecrets$1(result.secrets, workspace.root);
11776
+ console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
11777
+ for (const key of result.addedKeys) console.log(` + ${key}`);
11778
+ }
11779
+ }
11948
11780
  await pushSecrets$1(options.stage, workspace);
11949
11781
  console.log(`\n✓ Secrets pushed for stage "${options.stage}"`);
11950
11782
  } catch (error) {
@@ -11957,14 +11789,21 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
11957
11789
  const globalOptions = program.opts();
11958
11790
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11959
11791
  const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11960
- const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-BxFB34zW.cjs"));
11792
+ const { pullSecrets: pullSecrets$1 } = await Promise.resolve().then(() => require("./sync-RsnjXYwG.cjs"));
11961
11793
  const { writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
11794
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-7yarEvmK.cjs"));
11962
11795
  const { workspace } = await loadWorkspaceConfig$1();
11963
- const secrets = await pullSecrets$1(options.stage, workspace);
11796
+ let secrets = await pullSecrets$1(options.stage, workspace);
11964
11797
  if (!secrets) {
11965
11798
  console.error(`No remote secrets found for stage "${options.stage}".`);
11966
11799
  process.exit(1);
11967
11800
  }
11801
+ const result = reconcileMissingSecrets(secrets, workspace);
11802
+ if (result) {
11803
+ secrets = result.secrets;
11804
+ console.log(` Reconciled ${result.addedKeys.length} missing secret(s):`);
11805
+ for (const key of result.addedKeys) console.log(` + ${key}`);
11806
+ }
11968
11807
  await writeStageSecrets$1(secrets, workspace.root);
11969
11808
  console.log(`\n✓ Secrets pulled for stage "${options.stage}"`);
11970
11809
  } catch (error) {
@@ -11972,6 +11811,32 @@ program.command("secrets:pull").description("Pull secrets from remote provider (
11972
11811
  process.exit(1);
11973
11812
  }
11974
11813
  });
11814
+ program.command("secrets:reconcile").description("Backfill missing custom secrets from workspace config").option("--stage <stage>", "Stage name", "development").action(async (options) => {
11815
+ try {
11816
+ const globalOptions = program.opts();
11817
+ if (globalOptions.cwd) process.chdir(globalOptions.cwd);
11818
+ const { loadWorkspaceConfig: loadWorkspaceConfig$1 } = await Promise.resolve().then(() => require("./config.cjs"));
11819
+ const { reconcileMissingSecrets } = await Promise.resolve().then(() => require("./reconcile-7yarEvmK.cjs"));
11820
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await Promise.resolve().then(() => require("./storage-C7pmBq1u.cjs"));
11821
+ const { workspace } = await loadWorkspaceConfig$1();
11822
+ const secrets = await readStageSecrets$1(options.stage, workspace.root);
11823
+ if (!secrets) {
11824
+ console.error(`No secrets found for stage "${options.stage}". Run "gkm secrets:init --stage ${options.stage}" first.`);
11825
+ process.exit(1);
11826
+ }
11827
+ const result = reconcileMissingSecrets(secrets, workspace);
11828
+ if (!result) {
11829
+ console.log(`\n✓ Secrets for stage "${options.stage}" are up-to-date`);
11830
+ return;
11831
+ }
11832
+ await writeStageSecrets$1(result.secrets, workspace.root);
11833
+ console.log(`\n✓ Reconciled ${result.addedKeys.length} missing secret(s) for stage "${options.stage}":`);
11834
+ for (const key of result.addedKeys) console.log(` + ${key}`);
11835
+ } catch (error) {
11836
+ console.error(error instanceof Error ? error.message : "Command failed");
11837
+ process.exit(1);
11838
+ }
11839
+ });
11975
11840
  program.command("deploy").description("Deploy application to a provider").requiredOption("--provider <provider>", "Deploy provider (docker, dokploy, aws-lambda)").requiredOption("--stage <stage>", "Deployment stage (e.g., production, staging)").option("--tag <tag>", "Image tag (default: stage-timestamp)").option("--skip-push", "Skip pushing image to registry").option("--skip-build", "Skip build step (use existing build)").action(async (options) => {
11976
11841
  try {
11977
11842
  const globalOptions = program.opts();