@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.mjs CHANGED
@@ -8,8 +8,9 @@ import { getKeyPath, maskPassword, readStageSecrets, secretsExist, setCustomSecr
8
8
  import { DokployApi } from "./dokploy-api-2ldYoN3i.mjs";
9
9
  import { encryptSecrets } from "./encryption-BOH5M-f-.mjs";
10
10
  import { CachedStateProvider } from "./CachedStateProvider-BDq5WqSy.mjs";
11
- import { generateReactQueryCommand } from "./openapi-react-query-DaTMSPD5.mjs";
12
- import { isSSMConfigured, pullSecrets, pushSecrets } from "./sync-CHfhmXF3.mjs";
11
+ import { createStageSecrets, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, rotateServicePassword, writeDockerEnvFromSecrets } from "./fullstack-secrets-UZAFWuH4.mjs";
12
+ import { generateReactQueryCommand } from "./openapi-react-query-C4UdILaI.mjs";
13
+ import { isSSMConfigured, pullSecrets, pushSecrets } from "./sync-CbeKrnQV.mjs";
13
14
  import { createRequire } from "node:module";
14
15
  import { copyFileSync, existsSync, readFileSync, unlinkSync } from "node:fs";
15
16
  import { basename, dirname, join, parse, relative, resolve } from "node:path";
@@ -34,7 +35,7 @@ import prompts from "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$1, dirname: dirname$1 } = await import("node:path");
1837
1897
  const serverPath = join(this.appRoot, ".gkm", this.provider, "server.ts");
1838
- const relativeAppPath = relative$1(dirname$1(serverPath), join(dirname$1(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 import("./bundler-BxHyDhdt.mjs");
2155
+ const { bundleServer } = await import("./bundler-DQYjKFPm.mjs");
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$1.provider)) return config$1.provider;
2417
2432
  const provider = config$1.provider;
2418
2433
  if (provider === "hostinger") {
2419
- const { HostingerProvider } = await import("./HostingerProvider-DkahM5AP.mjs");
2434
+ const { HostingerProvider } = await import("./HostingerProvider-ANWchdiK.mjs");
2420
2435
  return new HostingerProvider();
2421
2436
  }
2422
2437
  if (provider === "route53") {
2423
- const { Route53Provider } = await import("./Route53Provider-Ckq_n5Be.mjs");
2438
+ const { Route53Provider } = await import("./Route53Provider-QoPgcXxn.mjs");
2424
2439
  const route53Config = config$1;
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: config$1, workspaceRoot, workspaceName } = options;
4670
4685
  if (!config$1) {
4671
- const { LocalStateProvider } = await import("./LocalStateProvider-DXIwWb7k.mjs");
4686
+ const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
4672
4687
  return new LocalStateProvider(workspaceRoot);
4673
4688
  }
4674
4689
  if (isStateProvider(config$1.provider)) return config$1.provider;
4675
4690
  const provider = config$1.provider;
4676
4691
  if (provider === "local") {
4677
- const { LocalStateProvider } = await import("./LocalStateProvider-DXIwWb7k.mjs");
4692
+ const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
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 import("./LocalStateProvider-DXIwWb7k.mjs");
4683
- const { SSMStateProvider } = await import("./SSMStateProvider-wddd0_-d.mjs");
4697
+ const { LocalStateProvider } = await import("./LocalStateProvider-Dp0KkRcw.mjs");
4698
+ const { SSMStateProvider } = await import("./SSMStateProvider-CksOTB8M.mjs");
4684
4699
  const { CachedStateProvider: CachedStateProvider$1 } = await import("./CachedStateProvider-CI61keQ1.mjs");
4685
4700
  const ssmConfig = config$1;
4686
4701
  const local = new LocalStateProvider(workspaceRoot);
@@ -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 import("./backup-provisioner-BAExdDtc.mjs");
5798
+ const { provisionBackupDestination } = await import("./backup-provisioner-BEXoHTuC.mjs");
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 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 = join(workspaceRoot, "docker", ".env");
6598
- await mkdir(dirname(envPath), { recursive: true });
6599
- await writeFile(envPath, envContent);
6600
- }
6601
-
6602
6423
  //#endregion
6603
6424
  //#region src/init/versions.ts
6604
6425
  const require$1 = createRequire(import.meta.url);
@@ -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
 
@@ -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 import("./config.mjs");
11946
- const { pushSecrets: pushSecrets$1 } = await import("./sync-BnqNNc6O.mjs");
11767
+ const { pushSecrets: pushSecrets$1 } = await import("./sync-6FoT41G3.mjs");
11768
+ const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
11769
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
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 import("./config.mjs");
11960
- const { pullSecrets: pullSecrets$1 } = await import("./sync-BnqNNc6O.mjs");
11792
+ const { pullSecrets: pullSecrets$1 } = await import("./sync-6FoT41G3.mjs");
11961
11793
  const { writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
11794
+ const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
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 import("./config.mjs");
11819
+ const { reconcileMissingSecrets } = await import("./reconcile-D2WCDQue.mjs");
11820
+ const { readStageSecrets: readStageSecrets$1, writeStageSecrets: writeStageSecrets$1 } = await import("./storage-Dx_jZbq6.mjs");
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();