@geekmidas/cli 10.0.0-alpha.27 → 10.0.0-alpha.28

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 (46) hide show
  1. package/dist/{HostingerProvider-BY26bWwB.mjs → HostingerProvider-Btxpto71.mjs} +2 -2
  2. package/dist/{HostingerProvider-BY26bWwB.mjs.map → HostingerProvider-Btxpto71.mjs.map} +1 -1
  3. package/dist/{bundler-mwH7WOb1.mjs → bundler-D5RcGmVM.mjs} +2 -2
  4. package/dist/{bundler-mwH7WOb1.mjs.map → bundler-D5RcGmVM.mjs.map} +1 -1
  5. package/dist/{config-7R_-WyHq.cjs → config-BTFLHfRb.cjs} +2 -2
  6. package/dist/{config-7R_-WyHq.cjs.map → config-BTFLHfRb.cjs.map} +1 -1
  7. package/dist/{config-DXrrYvob.mjs → config-Dqxa-_Ge.mjs} +2 -2
  8. package/dist/{config-DXrrYvob.mjs.map → config-Dqxa-_Ge.mjs.map} +1 -1
  9. package/dist/config.cjs +1 -1
  10. package/dist/config.mjs +1 -1
  11. package/dist/deploy/sniffer-loader.cjs +1 -1
  12. package/dist/{discover-B_HtYJCr.cjs → discover-BTh87DOO.cjs} +24 -2
  13. package/dist/{discover-B_HtYJCr.cjs.map → discover-BTh87DOO.cjs.map} +1 -1
  14. package/dist/{discover-0fhCbnk0.mjs → discover-CXGlQHm1.mjs} +24 -2
  15. package/dist/{discover-0fhCbnk0.mjs.map → discover-CXGlQHm1.mjs.map} +1 -1
  16. package/dist/{github-CQflOKKV.cjs → github-BUCXEl2v.cjs} +5 -5
  17. package/dist/{github-CQflOKKV.cjs.map → github-BUCXEl2v.cjs.map} +1 -1
  18. package/dist/{github-Ba5kXEJo.mjs → github-zkzu-ka2.mjs} +3 -3
  19. package/dist/{github-Ba5kXEJo.mjs.map → github-zkzu-ka2.mjs.map} +1 -1
  20. package/dist/index.cjs +740 -655
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.mjs +735 -652
  23. package/dist/index.mjs.map +1 -1
  24. package/dist/{openapi-3NLVvtz-.mjs → openapi-D-Lk5Hr5.mjs} +3 -3
  25. package/dist/{openapi-3NLVvtz-.mjs.map → openapi-D-Lk5Hr5.mjs.map} +1 -1
  26. package/dist/{openapi-rhJdy2GC.cjs → openapi-K4H5tHzA.cjs} +3 -3
  27. package/dist/{openapi-rhJdy2GC.cjs.map → openapi-K4H5tHzA.cjs.map} +1 -1
  28. package/dist/openapi.cjs +1 -1
  29. package/dist/openapi.mjs +1 -1
  30. package/dist/reconcile/public.cjs +1 -1
  31. package/dist/reconcile/public.d.cts +12 -0
  32. package/dist/reconcile/public.d.cts.map +1 -1
  33. package/dist/reconcile/public.d.mts +12 -0
  34. package/dist/reconcile/public.d.mts.map +1 -1
  35. package/dist/reconcile/public.mjs +1 -1
  36. package/dist/{workspace-DgeVgxmm.cjs → workspace-Cv48QwPK.cjs} +128 -9
  37. package/dist/workspace-Cv48QwPK.cjs.map +1 -0
  38. package/dist/{workspace-CUmR_k4J.mjs → workspace-cBobQVoA.mjs} +124 -11
  39. package/dist/workspace-cBobQVoA.mjs.map +1 -0
  40. package/package.json +14 -14
  41. package/dist/transfer-BV00TxZv.mjs +0 -122
  42. package/dist/transfer-BV00TxZv.mjs.map +0 -1
  43. package/dist/transfer-BYIn7z8m.cjs +0 -151
  44. package/dist/transfer-BYIn7z8m.cjs.map +0 -1
  45. package/dist/workspace-CUmR_k4J.mjs.map +0 -1
  46. package/dist/workspace-DgeVgxmm.cjs.map +0 -1
package/dist/index.mjs CHANGED
@@ -1,14 +1,13 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  import { n as __require, t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
3
- import { i as DEFAULT_CACHE, r as ConstructGenerator, s as isPartitionedRoutes, t as discover } from "./discover-0fhCbnk0.mjs";
4
- import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-DXrrYvob.mjs";
3
+ import { i as DEFAULT_CACHE, r as ConstructGenerator, s as isPartitionedRoutes, t as discover } from "./discover-CXGlQHm1.mjs";
4
+ import { a as loadConfig, c as parseModuleConfig, i as loadAppConfig, l as appKey, r as getAppNameFromCwd, s as loadWorkspaceConfig, u as derivedApps } from "./config-Dqxa-_Ge.mjs";
5
5
  import { D as assertDeployedStage, E as TEST_STAGE, O as deployedProblems, T as InvalidStages, a as getAppBuildOrder, b as getDeployTargetError, k as stageProblems, m as stripPublicPrefix, o as getAppGkmConfig, p as getPublicEnvPrefix, t as allConstructGlobs, x as isDeployTargetSupported } from "./workspace-T4IVGSoB.mjs";
6
6
  import { c as getKeyPath, i as setCustomSecret, n as readStageSecrets, o as toEmbeddableSecrets, r as secretsExist, s as writeStageSecrets, t as maskPassword } from "./storage-TP0x118M.mjs";
7
- import { _ as isPortAvailable, b as prepareEntryCredentials, c as applyPostgres, d as eventsBackendFor, f as providerOf, g as findAvailablePort, h as createEntryWrapper, i as reconcileWorkspace, l as resourceName, m as createCredentialsPreload, n as constructGlobs, o as COMPOSE_PATH, p as storageBackendFor, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as cacheBackendFor, v as loadEnvFiles, y as loadSecretsForApp } from "./workspace-CUmR_k4J.mjs";
8
- import { a as openapiCommand, c as EndpointGenerator, l as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, s as OpenApiTsGenerator, t as OPENAPI_OUTPUT_PATH$1 } from "./openapi-3NLVvtz-.mjs";
7
+ import { _ as findAvailablePort, b as loadSecretsForApp, c as applyPostgres, d as cacheBackendFor, f as eventsBackendFor, g as createEntryWrapper, h as createCredentialsPreload, i as reconcileWorkspace, l as resourceName, m as storageBackendFor, n as constructGlobs, o as COMPOSE_PATH, p as providerOf, r as derivedContainers, s as LOCAL_CA_PATH, t as backendsOf, u as rootSite, v as isPortAvailable, x as prepareEntryCredentials, y as loadEnvFiles } from "./workspace-cBobQVoA.mjs";
8
+ import { a as openapiCommand, c as EndpointGenerator, l as runtimeFor, n as generateOpenApi, o as resolveOpenApiConfig, s as OpenApiTsGenerator } from "./openapi-D-Lk5Hr5.mjs";
9
9
  import { i as withRoutes, r as withCompute } from "./emit-Bzq7JsMP.mjs";
10
- import { a as createStageSecrets, c as generateServiceCredentials, i as writeDockerEnvFromSecrets, l as rotateServicePassword, o as generateConnectionUrls, r as generateFullstackCustomSecrets, s as generateSecurePassword } from "./reconcile-DhbCvh4K.mjs";
11
- import { a as isRemoteStore, n as pullStageSecrets, r as pushStageSecrets, t as NoStoredSecrets } from "./transfer-BV00TxZv.mjs";
10
+ import { a as createStageSecrets, c as generateServiceCredentials, i as writeDockerEnvFromSecrets, l as rotateServicePassword, o as generateConnectionUrls, r as generateFullstackCustomSecrets, s as generateSecurePassword, t as reconcileMissingSecrets } from "./reconcile-DhbCvh4K.mjs";
12
11
  import { createRequire } from "node:module";
13
12
  import { Command } from "commander";
14
13
  import { stdin, stdout } from "node:process";
@@ -28,17 +27,17 @@ import { Client } from "pg";
28
27
  import { parse as parse$1, parseDocument } from "yaml";
29
28
  import { promisify } from "node:util";
30
29
  import { cacheTableStatements } from "@geekmidas/cache/postgres";
30
+ import prompts from "prompts";
31
31
  import { Cron } from "@geekmidas/constructs/crons";
32
32
  import { Function } from "@geekmidas/constructs/functions";
33
33
  import { Queue } from "@geekmidas/constructs/queue";
34
34
  import { Subscriber } from "@geekmidas/constructs/subscribers";
35
35
  import { Topic } from "@geekmidas/constructs/topic";
36
36
  import { lookup } from "node:dns/promises";
37
- import prompts from "prompts";
38
37
  import { EnvironmentParser } from "@geekmidas/envkit";
39
38
  import { serviceContext } from "@geekmidas/services";
40
39
  //#region package.json
41
- var version = "10.0.0-alpha.27";
40
+ var version = "10.0.0-alpha.28";
42
41
  //#endregion
43
42
  //#region src/auth/credentials.ts
44
43
  /**
@@ -405,8 +404,182 @@ function normalizeRoutes(routes) {
405
404
  return Array.isArray(routes) ? routes : [routes];
406
405
  }
407
406
  //#endregion
408
- //#region src/exec/index.ts
407
+ //#region src/trust/index.ts
408
+ /**
409
+ * Installing the local edge's certificate authority into the OS trust store.
410
+ *
411
+ * `NODE_EXTRA_CA_CERTS` covers everything gkm starts — the dev server, the test
412
+ * suite, anything under `gkm exec` — because reconcile injects it. A browser
413
+ * reads none of that: it asks the operating system, so until the root is in the
414
+ * system store a local `https://` address is "not secure", which is true and
415
+ * unhelpful.
416
+ *
417
+ * A separate command rather than part of `gkm setup`, for one reason: this
418
+ * needs `sudo`, and a setup that silently escalates on every run is a worse
419
+ * trade than one that tells you what to type. Caddy's own `caddy trust` draws
420
+ * the line in the same place.
421
+ */
409
422
  const logger$12 = console;
423
+ const run = promisify(execFile);
424
+ /**
425
+ * How each platform is told about a new authority.
426
+ *
427
+ * Separate rather than shelled through one script because the failure modes
428
+ * differ and the message should name the real one: macOS refuses without an
429
+ * admin password, Debian needs the file in a specific directory before its
430
+ * updater will look at it.
431
+ */
432
+ function installation(platform, certificate) {
433
+ if (platform === "darwin") return {
434
+ describe: "the System keychain",
435
+ steps: [["sudo", [
436
+ "security",
437
+ "add-trusted-cert",
438
+ "-d",
439
+ "-r",
440
+ "trustRoot",
441
+ "-k",
442
+ "/Library/Keychains/System.keychain",
443
+ certificate
444
+ ]]]
445
+ };
446
+ if (platform === "linux") {
447
+ const debian = existsSync("/usr/local/share/ca-certificates");
448
+ return {
449
+ describe: debian ? "the system CA bundle" : "the system trust anchors",
450
+ steps: debian ? [["sudo", [
451
+ "cp",
452
+ certificate,
453
+ "/usr/local/share/ca-certificates/gkm-local-ca.crt"
454
+ ]], ["sudo", ["update-ca-certificates"]]] : [["sudo", [
455
+ "cp",
456
+ certificate,
457
+ "/etc/pki/ca-trust/source/anchors/gkm-local-ca.crt"
458
+ ]], ["sudo", ["update-ca-trust"]]]
459
+ };
460
+ }
461
+ throw new UnsupportedPlatform(platform, certificate);
462
+ }
463
+ /**
464
+ * Trust the certificate authority this project's edge issues from.
465
+ *
466
+ * Idempotent in effect: installing a root that is already trusted replaces the
467
+ * same entry, so running it twice is not an error.
468
+ */
469
+ async function trustCommand(options = {}) {
470
+ const { workspace } = await loadWorkspaceConfig();
471
+ const certificate = join(workspace.root, LOCAL_CA_PATH);
472
+ if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
473
+ const { describe, steps } = installation(process.platform, certificate);
474
+ logger$12.log(`\n🔐 Trusting the local authority in ${describe}`);
475
+ logger$12.log(` ${certificate}\n`);
476
+ if (options.dryRun) {
477
+ for (const [command, args] of steps) logger$12.log(` ${command} ${args.join(" ")}`);
478
+ return;
479
+ }
480
+ logger$12.log(" This needs an admin password.\n");
481
+ for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
482
+ logger$12.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
483
+ }
484
+ /**
485
+ * Trust the authority if this machine lacks it and its owner agrees.
486
+ *
487
+ * A machine's answer, not the project's: whether a laptop installs a root
488
+ * certificate is its owner's call, so the decision is `--yes`, then what this
489
+ * machine previously answered, then a prompt. Never a silent `sudo` — that is
490
+ * the one thing a setup command should not do on somebody's behalf.
491
+ *
492
+ * Silent and free when the machine already trusts it, which is what makes it
493
+ * acceptable to call on every `gkm setup`.
494
+ */
495
+ async function ensureTrusted(root, url, options = {}) {
496
+ if (await isTrusted(url)) return;
497
+ if (!options.assumeYes) {
498
+ if (!process.stdin.isTTY) {
499
+ logger$12.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
500
+ return;
501
+ }
502
+ if (await declined(root)) return;
503
+ const { install } = await prompts({
504
+ type: "confirm",
505
+ name: "install",
506
+ message: "Trust this project's local certificate authority, so a browser accepts its https addresses? (needs sudo)",
507
+ initial: false
508
+ });
509
+ if (!install) {
510
+ await recordDeclined(root);
511
+ logger$12.log(" Skipped. Run \"gkm trust\" later.");
512
+ return;
513
+ }
514
+ }
515
+ await trustCommand();
516
+ }
517
+ /** Where a declined answer is remembered, so setup asks once and not again. */
518
+ const TRUST_STATE = ".gkm/trust.json";
519
+ async function declined(root) {
520
+ try {
521
+ const raw = await readFile(join(root, TRUST_STATE), "utf-8");
522
+ return JSON.parse(raw).declined === true;
523
+ } catch {
524
+ return false;
525
+ }
526
+ }
527
+ async function recordDeclined(root) {
528
+ const path = join(root, TRUST_STATE);
529
+ await mkdir(dirname(path), { recursive: true });
530
+ await writeFile(path, `${JSON.stringify({ declined: true }, null, 2)}\n`);
531
+ }
532
+ /**
533
+ * Whether this machine already trusts the local authority.
534
+ *
535
+ * Asked by connecting rather than by reading a keychain: what matters is
536
+ * whether a client with no special configuration verifies the edge, and that is
537
+ * one platform-neutral question instead of three platform-specific ones. It is
538
+ * also the exact thing a browser is about to do.
539
+ *
540
+ * A child process, because `NODE_EXTRA_CA_CERTS` is read once at startup and
541
+ * this process almost certainly has it set — asking in-process would answer
542
+ * "yes" for everyone.
543
+ */
544
+ async function isTrusted(url) {
545
+ const { NODE_EXTRA_CA_CERTS: _ignored, ...env } = process.env;
546
+ try {
547
+ await run(process.execPath, [
548
+ "--use-system-ca",
549
+ "-e",
550
+ `fetch(${JSON.stringify(url)}).then(()=>0,()=>process.exit(1))`
551
+ ], {
552
+ env,
553
+ timeout: 15e3
554
+ });
555
+ return true;
556
+ } catch {
557
+ return false;
558
+ }
559
+ }
560
+ /** The edge has not run, so there is no authority to trust yet. */
561
+ var NoLocalAuthority = class extends Error {
562
+ path;
563
+ constructor(path) {
564
+ super(`No local certificate authority at ${path}. It is generated the first time the edge starts, so run "gkm setup" (or "gkm dev") once — and if this project declares no file server, nothing needs one.`);
565
+ this.path = path;
566
+ this.name = "NoLocalAuthority";
567
+ }
568
+ };
569
+ /** A platform with no trust store this knows how to write to. */
570
+ var UnsupportedPlatform = class extends Error {
571
+ platform;
572
+ certificate;
573
+ constructor(platform, certificate) {
574
+ super(`gkm does not know how to install a certificate authority on ${platform}. The root is at ${certificate} — add it to the trust store by hand, or point a client at it with NODE_EXTRA_CA_CERTS.`);
575
+ this.platform = platform;
576
+ this.certificate = certificate;
577
+ this.name = "UnsupportedPlatform";
578
+ }
579
+ };
580
+ //#endregion
581
+ //#region src/exec/index.ts
582
+ const logger$11 = console;
410
583
  /** `gkm exec` was given nothing to run. */
411
584
  var NoCommandSpecified = class extends Error {
412
585
  constructor() {
@@ -429,17 +602,17 @@ async function execCommand(commandArgs, options = {}) {
429
602
  const cwd = options.cwd ?? process.cwd();
430
603
  if (!commandArgs[0]) throw new NoCommandSpecified();
431
604
  const defaultEnv = loadEnvFiles(".env");
432
- if (defaultEnv.loaded.length > 0) logger$12.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
605
+ if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
433
606
  const { credentials, secretsJsonPath, appName } = await prepareEntryCredentials({ cwd });
434
- if (appName) logger$12.log(`📦 App: ${appName}`);
607
+ if (appName) logger$11.log(`📦 App: ${appName}`);
435
608
  const secretCount = Object.keys(credentials).filter((k) => k !== "PORT").length;
436
- if (secretCount > 0) logger$12.log(`🔐 Loaded ${secretCount} secret(s)`);
609
+ if (secretCount > 0) logger$11.log(`🔐 Loaded ${secretCount} secret(s)`);
437
610
  const preloadDir = join(cwd, ".gkm");
438
611
  await mkdir(preloadDir, { recursive: true });
439
612
  const preloadPath = join(preloadDir, "credentials-preload.mjs");
440
613
  await createCredentialsPreload(preloadPath, secretsJsonPath);
441
614
  const [cmd, ...args] = commandArgs;
442
- logger$12.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
615
+ logger$11.log(`🚀 Running: ${[cmd, ...args].join(" ")}`);
443
616
  const nodeOptions = [(process.env.NODE_OPTIONS ?? "").replace(/--import[= ]\S+/g, "").trim(), `--import=${preloadPath}`].filter(Boolean).join(" ");
444
617
  const child = spawn(cmd, args, {
445
618
  cwd,
@@ -453,7 +626,7 @@ async function execCommand(commandArgs, options = {}) {
453
626
  const exitCode = await new Promise((resolve) => {
454
627
  child.on("close", (code) => resolve(code ?? 0));
455
628
  child.on("error", (error) => {
456
- logger$12.error(`Failed to run command: ${error.message}`);
629
+ logger$11.error(`Failed to run command: ${error.message}`);
457
630
  resolve(1);
458
631
  });
459
632
  });
@@ -461,7 +634,7 @@ async function execCommand(commandArgs, options = {}) {
461
634
  }
462
635
  //#endregion
463
636
  //#region src/dev/index.ts
464
- const logger$11 = console;
637
+ const logger$10 = console;
465
638
  /**
466
639
  * Normalize telescope configuration
467
640
  * @internal Exported for testing
@@ -555,7 +728,7 @@ function getProductionConfigFromGkm(config) {
555
728
  async function devCommand(options) {
556
729
  if (options.entry) return entryDevCommand(options);
557
730
  const defaultEnv = loadEnvFiles(".env");
558
- if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
731
+ if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
559
732
  const appName = getAppNameFromCwd();
560
733
  let config;
561
734
  let appRoot = process.cwd();
@@ -571,9 +744,8 @@ async function devCommand(options) {
571
744
  workspaceAppName = appConfig.appName;
572
745
  workspaceAppPort = appConfig.app.port;
573
746
  workspace = appConfig.workspace;
574
- logger$11.log(`📦 Running app: ${appConfig.appName} on port ${workspaceAppPort}`);
575
747
  if (appConfig.app.entry) {
576
- logger$11.log(`📄 Using entry point: ${appConfig.app.entry}`);
748
+ logger$10.log(`📄 Using entry point: ${appConfig.app.entry}`);
577
749
  return entryDevCommand({
578
750
  ...options,
579
751
  entry: appConfig.app.entry,
@@ -583,39 +755,27 @@ async function devCommand(options) {
583
755
  }
584
756
  } catch {
585
757
  const loadedConfig = await loadWorkspaceConfig();
586
- if (loadedConfig.type === "workspace") {
587
- logger$11.log("📦 Detected workspace configuration");
588
- return workspaceDevCommand(loadedConfig.workspace, options);
589
- }
758
+ if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
590
759
  config = loadedConfig.raw;
591
760
  workspace = loadedConfig.workspace;
592
761
  }
593
762
  else {
594
763
  const loadedConfig = await loadWorkspaceConfig();
595
- if (loadedConfig.type === "workspace") {
596
- logger$11.log("📦 Detected workspace configuration");
597
- return workspaceDevCommand(loadedConfig.workspace, options);
598
- }
764
+ if (loadedConfig.type === "workspace") return workspaceDevCommand(loadedConfig.workspace, options);
599
765
  config = loadedConfig.raw;
600
766
  workspace = loadedConfig.workspace;
601
767
  }
602
768
  if (config.env) {
603
769
  const { loaded, missing } = loadEnvFiles(config.env, appRoot);
604
- if (loaded.length > 0) logger$11.log(`📦 Loaded env: ${loaded.join(", ")}`);
605
- if (missing.length > 0) logger$11.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
770
+ if (loaded.length > 0) logger$10.log(`📦 Loaded env: ${loaded.join(", ")}`);
771
+ if (missing.length > 0) logger$10.warn(`⚠️ Missing env files: ${missing.join(", ")}`);
606
772
  }
607
773
  const resolved = resolveProviders(config, { provider: "server" });
608
- logger$11.log("🚀 Starting development server...");
609
- logger$11.log(`Loading constructs from: ${config.constructs}`);
610
774
  const telescope = normalizeTelescopeConfig(config.telescope);
611
- if (telescope) logger$11.log(`🔭 Telescope enabled at ${telescope.path}`);
612
775
  const studio = normalizeStudioConfig(config.studio);
613
- if (studio) logger$11.log(`🗄️ Studio enabled at ${studio.path}`);
614
776
  const hooks = normalizeHooksConfig(config.hooks, appRoot);
615
- if (hooks) logger$11.log(`🪝 Server hooks enabled from ${config.hooks?.server}`);
616
777
  const enableOpenApi = resolveOpenApiConfig(config).enabled || resolved.enableOpenApi;
617
- if (enableOpenApi) logger$11.log(`📄 OpenAPI output: ${OPENAPI_OUTPUT_PATH$1}`);
618
- const build = (bustCache = false) => buildApp({
778
+ const build = (bustCache = false) => quietly(() => buildApp({
619
779
  config,
620
780
  workspaceRoot: workspace?.root ?? secretsRoot,
621
781
  appRoot,
@@ -628,18 +788,21 @@ async function devCommand(options) {
628
788
  skipBundle: true,
629
789
  bustCache,
630
790
  serveEmpty: true
631
- });
791
+ }));
632
792
  const initial = await build();
633
793
  const runtime = config.runtime ?? "node";
634
794
  let secretsJsonPath;
795
+ let publicUrl;
635
796
  const appSecrets = await loadSecretsForApp(secretsRoot, config.stages.local, workspaceAppName);
636
797
  if (workspace) {
637
798
  const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
638
799
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
639
- logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
640
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
800
+ logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
801
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
641
802
  }
642
803
  Object.assign(appSecrets, reconciled.env);
804
+ const own = reconciled.plan.resources.find((r) => (r.kind === "rest-api" || r.kind === "site") && appKey(r.id) === workspaceAppName);
805
+ publicUrl = own ? reconciled.env[own.envKey] : void 0;
643
806
  }
644
807
  if (Object.keys(appSecrets).length > 0) {
645
808
  const secretsDir = join(secretsRoot, ".gkm");
@@ -647,13 +810,13 @@ async function devCommand(options) {
647
810
  const secretsFileName = workspaceAppName ? `dev-secrets-${workspaceAppName}.json` : "dev-secrets.json";
648
811
  secretsJsonPath = join(secretsDir, secretsFileName);
649
812
  await writeFile(secretsJsonPath, JSON.stringify(appSecrets, null, 2));
650
- logger$11.log(`🔐 Loaded ${Object.keys(appSecrets).length} secret(s)`);
651
813
  }
652
814
  const devServer = new DevServer(resolved.providers[0], options.port ?? workspaceAppPort ?? 3e3, options.portExplicit ?? workspaceAppPort !== void 0, enableOpenApi, telescope, studio, runtime, appRoot, secretsJsonPath, initial.selfServing !== void 0);
815
+ devServer.label = workspaceAppName ?? "server";
816
+ devServer.publicUrl = publicUrl;
653
817
  await devServer.start();
654
818
  const hooksFile = (config.hooks?.server?.split("#"))?.[0];
655
819
  const normalizedPatterns = [config.constructs, ...hooksFile ? [hooksFile.endsWith(".ts") ? hooksFile : `${hooksFile}.ts`] : []].flat().filter((p) => typeof p === "string").map((p) => p.startsWith("./") ? p.slice(2) : p);
656
- logger$11.log(`👀 Watching for changes in: ${normalizedPatterns.join(", ")}`);
657
820
  const resolvedFiles = await fg(normalizedPatterns, {
658
821
  cwd: appRoot,
659
822
  absolute: false,
@@ -662,34 +825,28 @@ async function devCommand(options) {
662
825
  const dirsToWatch = [...new Set(resolvedFiles.map((f) => {
663
826
  return f.split("/").slice(0, -1).join("/");
664
827
  }))];
665
- logger$11.log(`📁 Found ${resolvedFiles.length} files in ${dirsToWatch.length} directories`);
666
828
  const watcher = chokidar.watch([...resolvedFiles, ...dirsToWatch], {
667
829
  ignored: /(^|[/\\])\../,
668
830
  persistent: true,
669
831
  ignoreInitial: true,
670
832
  cwd: appRoot
671
833
  });
672
- watcher.on("ready", () => {
673
- logger$11.log("🔍 File watcher ready");
674
- });
675
834
  watcher.on("error", (error) => {
676
- logger$11.error("❌ Watcher error:", error);
835
+ logger$10.error("❌ Watcher error:", error);
677
836
  });
678
837
  let rebuildTimeout = null;
679
838
  watcher.on("change", async (path) => {
680
- logger$11.log(`📝 File changed: ${path}`);
839
+ logger$10.log(`🔄 ${path} changed — rebuilding`);
681
840
  if (rebuildTimeout) clearTimeout(rebuildTimeout);
682
841
  rebuildTimeout = setTimeout(async () => {
683
842
  try {
684
- logger$11.log("🔄 Rebuilding...");
685
843
  const rebuilt = await build(true);
686
- logger$11.log("✅ Rebuild complete, restarting server...");
687
844
  devServer.selfServing = rebuilt.selfServing !== void 0;
688
845
  await devServer.restart();
689
846
  } catch (error) {
690
847
  const err = error;
691
- logger$11.error(`❌ Rebuild failed: ${err.message}`);
692
- if (err.stack) logger$11.error(err.stack);
848
+ logger$10.error(`❌ Rebuild failed: ${err.message}`);
849
+ if (err.stack) logger$10.error(err.stack);
693
850
  }
694
851
  }, 300);
695
852
  });
@@ -697,9 +854,9 @@ async function devCommand(options) {
697
854
  const shutdown = () => {
698
855
  if (isShuttingDown) return;
699
856
  isShuttingDown = true;
700
- logger$11.log("\n🛑 Shutting down...");
857
+ logger$10.log("\n🛑 Shutting down...");
701
858
  Promise.all([watcher.close(), devServer.stop()]).catch((err) => {
702
- logger$11.error("Error during shutdown:", err);
859
+ logger$10.error("Error during shutdown:", err);
703
860
  }).finally(() => {
704
861
  process.exit(0);
705
862
  });
@@ -885,12 +1042,8 @@ async function loadDevSecrets(workspace) {
885
1042
  const stage = workspace.stages.local;
886
1043
  if (secretsExist(stage, workspace.root)) {
887
1044
  const secrets = await readStageSecrets(stage, workspace.root);
888
- if (secrets) {
889
- logger$11.log(`🔐 Loading secrets from stage: ${stage}`);
890
- return toEmbeddableSecrets(secrets);
891
- }
1045
+ if (secrets) return toEmbeddableSecrets(secrets);
892
1046
  }
893
- logger$11.warn(`⚠️ Secrets enabled but no "${stage}" secrets found. Run "gkm setup" to initialize the local stage`);
894
1047
  return {};
895
1048
  }
896
1049
  /**
@@ -904,38 +1057,30 @@ async function loadDevSecrets(workspace) {
904
1057
  */
905
1058
  async function workspaceDevCommand(workspace, options) {
906
1059
  const appCount = Object.keys(workspace.apps).length;
907
- const backendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "backend");
908
1060
  const frontendApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "web");
909
- const mobileApps = Object.entries(workspace.apps).filter(([_, app]) => app.type === "mobile");
910
- logger$11.log(`\n🚀 Starting workspace: ${workspace.name}`);
911
- const counts = [`${backendApps.length} backend`, `${frontendApps.length} web`];
912
- if (mobileApps.length > 0) counts.push(`${mobileApps.length} mobile`);
913
- logger$11.log(` ${counts.join(", ")} app(s)`);
914
1061
  const conflicts = checkPortConflicts(workspace);
915
1062
  if (conflicts.length > 0) {
916
- for (const conflict of conflicts) logger$11.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
1063
+ for (const conflict of conflicts) logger$10.error(`❌ Port conflict: Apps "${conflict.app1}" and "${conflict.app2}" both use port ${conflict.port}`);
917
1064
  throw new Error("Port conflicts detected. Please assign unique ports to each app.");
918
1065
  }
919
1066
  if (frontendApps.length > 0) {
920
- logger$11.log("\n🔍 Validating frontend apps...");
921
1067
  const validationResults = await validateFrontendApps(workspace);
922
1068
  let hasErrors = false;
923
1069
  for (const result of validationResults) {
924
1070
  if (!result.valid) {
925
1071
  hasErrors = true;
926
- logger$11.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
927
- for (const error of result.errors) logger$11.error(` • ${error}`);
1072
+ logger$10.error(`\n❌ Frontend app "${result.appName}" validation failed:`);
1073
+ for (const error of result.errors) logger$10.error(` • ${error}`);
928
1074
  }
929
- for (const warning of result.warnings) logger$11.warn(` ⚠️ ${result.appName}: ${warning}`);
1075
+ for (const warning of result.warnings) logger$10.warn(` ⚠️ ${result.appName}: ${warning}`);
930
1076
  }
931
1077
  if (hasErrors) throw new Error("Frontend app validation failed. Fix the issues above and try again.");
932
- logger$11.log("✅ Frontend apps validated");
933
1078
  }
934
1079
  const rawSecrets = await loadDevSecrets(workspace);
935
1080
  const reconciled = await reconcileWorkspace(workspace, { stage: workspace.stages.local });
936
1081
  if (reconciled.changed && reconciled.plan.containers.length > 0) {
937
- logger$11.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
938
- for (const [container, address] of Object.entries(reconciled.addresses)) logger$11.log(` ${container}: ${address}`);
1082
+ logger$10.log(`🐳 Services: ${reconciled.plan.containers.join(", ")}`);
1083
+ for (const [container, address] of Object.entries(reconciled.addresses)) logger$10.log(` ${container}: ${address}`);
939
1084
  }
940
1085
  const secretsEnv = {
941
1086
  ...rawSecrets,
@@ -947,11 +1092,8 @@ async function workspaceDevCommand(workspace, options) {
947
1092
  const address = reconciled.env[resource.envKey];
948
1093
  if (address) appUrls[resource.id] = address;
949
1094
  }
950
- if (Object.keys(secretsEnv).length > 0) logger$11.log(` Loaded ${Object.keys(secretsEnv).length} secret(s)`);
951
- if (Object.keys(appUrls).length > 0) {
952
- logger$11.log("🔒 App URLs:");
953
- for (const [id, address] of Object.entries(appUrls)) logger$11.log(` ${id}: ${address}`);
954
- }
1095
+ const firstUrl = Object.values(appUrls).find((url) => url.startsWith("https://"));
1096
+ if (firstUrl) await ensureTrusted(workspace.root, firstUrl);
955
1097
  let turboFilter = [];
956
1098
  if (options.app) {
957
1099
  if (!workspace.apps[options.app]) {
@@ -959,25 +1101,23 @@ async function workspaceDevCommand(workspace, options) {
959
1101
  throw new Error(`App "${options.app}" not found. Available apps: ${appNames}`);
960
1102
  }
961
1103
  turboFilter = ["--filter", appPackageName(workspace, options.app) ?? options.app];
962
- logger$11.log(`\n🎯 Running single app: ${options.app}`);
963
- } else if (options.filter) {
964
- turboFilter = ["--filter", options.filter];
965
- logger$11.log(`\n🔍 Using filter: ${options.filter}`);
966
- } else {
1104
+ } else if (options.filter) turboFilter = ["--filter", options.filter];
1105
+ else {
967
1106
  const { filters, unpackaged } = turboFilters(workspace);
968
1107
  turboFilter = filters.flatMap((name) => ["--filter", name]);
969
- if (unpackaged.length > 0) logger$11.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
970
- logger$11.log(`\n🎯 Running all ${appCount} apps`);
1108
+ if (unpackaged.length > 0) logger$10.warn(`⚠️ No package.json with a name for: ${unpackaged.join(", ")} — turbo cannot run ${unpackaged.length === 1 ? "it" : "them"}.`);
971
1109
  }
972
1110
  const buildOrder = getAppBuildOrder(workspace);
973
- logger$11.log("\n📋 Apps (in dependency order):");
1111
+ const width = Math.max(...buildOrder.map((name) => name.length));
1112
+ logger$10.log(`\n${workspace.name}: ${appCount} app(s)`);
974
1113
  for (const appName of buildOrder) {
975
1114
  const app = workspace.apps[appName];
976
1115
  if (!app) continue;
977
- const deps = app.dependencies.length > 0 ? ` (depends on: ${app.dependencies.join(", ")})` : "";
978
- const icon = app.type === "backend" ? "🔧" : app.type === "mobile" ? "📱" : "🌐";
979
- logger$11.log(` ${icon} ${appName} → http://localhost:${app.port}${deps}`);
1116
+ const local = `http://localhost:${app.port}`;
1117
+ const url = Object.entries(appUrls).find(([id]) => appKey(id) === appName)?.[1];
1118
+ logger$10.log(` ${appName.padEnd(width)} ${url ? `${url} -> ${local}` : local}`);
980
1119
  }
1120
+ logger$10.log("");
981
1121
  const configFiles = [
982
1122
  "gkm.config.ts",
983
1123
  "gkm.config.js",
@@ -1008,7 +1148,6 @@ async function workspaceDevCommand(workspace, options) {
1008
1148
  });
1009
1149
  }
1010
1150
  if (held.length > 0) throw new WorkspacePortsInUse(held);
1011
- logger$11.log("\n🏃 Starting turbo run dev...\n");
1012
1151
  const turboProcess = spawn("pnpm", [
1013
1152
  "turbo",
1014
1153
  "run",
@@ -1024,7 +1163,7 @@ async function workspaceDevCommand(workspace, options) {
1024
1163
  const shutdown = () => {
1025
1164
  if (isShuttingDown) return;
1026
1165
  isShuttingDown = true;
1027
- logger$11.log("\n🛑 Shutting down workspace...");
1166
+ logger$10.log("\n🛑 Shutting down workspace...");
1028
1167
  const pid = turboProcess.pid;
1029
1168
  if (pid) try {
1030
1169
  process.kill(-pid, "SIGTERM");
@@ -1048,7 +1187,7 @@ async function workspaceDevCommand(workspace, options) {
1048
1187
  process.on("SIGTERM", shutdown);
1049
1188
  return new Promise((resolve, reject) => {
1050
1189
  turboProcess.on("error", (error) => {
1051
- logger$11.error("❌ Turbo error:", error);
1190
+ logger$10.error("❌ Turbo error:", error);
1052
1191
  reject(error);
1053
1192
  });
1054
1193
  turboProcess.on("exit", (code) => {
@@ -1067,11 +1206,11 @@ async function entryDevCommand(options) {
1067
1206
  const entryPath = resolve(process.cwd(), entry);
1068
1207
  if (!existsSync(entryPath)) throw new Error(`Entry file not found: ${entryPath}`);
1069
1208
  const defaultEnv = loadEnvFiles(".env");
1070
- if (defaultEnv.loaded.length > 0) logger$11.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1209
+ if (defaultEnv.loaded.length > 0) logger$10.log(`📦 Loaded env: ${defaultEnv.loaded.join(", ")}`);
1071
1210
  const { credentials, resolvedPort, secretsJsonPath, appName } = await prepareEntryCredentials({ explicitPort: options.portExplicit ? options.port : void 0 });
1072
- if (appName) logger$11.log(`📦 App: ${appName} (port ${resolvedPort})`);
1073
- logger$11.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
1074
- if (Object.keys(credentials).length > 1) logger$11.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
1211
+ if (appName) logger$10.log(`📦 App: ${appName} (port ${resolvedPort})`);
1212
+ logger$10.log(`🚀 Starting entry file: ${entry} on port ${resolvedPort}`);
1213
+ if (Object.keys(credentials).length > 1) logger$10.log(`🔐 Loaded ${Object.keys(credentials).length - 1} secret(s) + PORT`);
1075
1214
  const wrapperDir = join(process.cwd(), ".gkm");
1076
1215
  await mkdir(wrapperDir, { recursive: true });
1077
1216
  const wrapperPath = join(wrapperDir, "entry-wrapper.ts");
@@ -1082,7 +1221,7 @@ async function entryDevCommand(options) {
1082
1221
  const shutdown = () => {
1083
1222
  if (isShuttingDown) return;
1084
1223
  isShuttingDown = true;
1085
- logger$11.log("\n🛑 Shutting down...");
1224
+ logger$10.log("\n🛑 Shutting down...");
1086
1225
  runner.stop();
1087
1226
  process.exit(0);
1088
1227
  };
@@ -1118,14 +1257,14 @@ var EntryRunner = class {
1118
1257
  });
1119
1258
  let restartTimeout = null;
1120
1259
  this.watcher.on("change", (path) => {
1121
- logger$11.log(`📝 File changed: ${path}`);
1260
+ logger$10.log(`📝 File changed: ${path}`);
1122
1261
  if (restartTimeout) clearTimeout(restartTimeout);
1123
1262
  restartTimeout = setTimeout(async () => {
1124
- logger$11.log("🔄 Restarting...");
1263
+ logger$10.log("🔄 Restarting...");
1125
1264
  await this.restart();
1126
1265
  }, 300);
1127
1266
  });
1128
- logger$11.log(`👀 Watching for changes in: ${watchDir}`);
1267
+ logger$10.log(`👀 Watching for changes in: ${watchDir}`);
1129
1268
  }
1130
1269
  }
1131
1270
  async runProcess() {
@@ -1133,24 +1272,24 @@ var EntryRunner = class {
1133
1272
  ...process.env,
1134
1273
  PORT: String(this.port)
1135
1274
  };
1136
- this.childProcess = spawn("npx", ["tsx", this.wrapperPath], {
1275
+ this.childProcess = spawn(...tsxCommand(dirname(this.wrapperPath), [this.wrapperPath]), {
1137
1276
  stdio: "inherit",
1138
1277
  env,
1139
1278
  detached: true
1140
1279
  });
1141
1280
  this.isRunning = true;
1142
1281
  this.childProcess.on("error", (error) => {
1143
- logger$11.error("❌ Process error:", error);
1282
+ logger$10.error("❌ Process error:", error);
1144
1283
  });
1145
1284
  this.childProcess.on("exit", (code) => {
1146
- if (code !== null && code !== 0 && code !== 143) logger$11.error(`❌ Process exited with code ${code}`);
1285
+ if (code !== null && code !== 0 && code !== 143) logger$10.error(`❌ Process exited with code ${code}`);
1147
1286
  this.isRunning = false;
1148
1287
  });
1149
1288
  await new Promise((resolve) => setTimeout(resolve, 500));
1150
1289
  if (this.isRunning) {
1151
- logger$11.log("");
1152
- logger$11.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
1153
- logger$11.log("");
1290
+ logger$10.log("");
1291
+ logger$10.log(` \x1b[32m✓ Ready\x1b[0m at \x1b[36mhttp://localhost:${this.port}\x1b[0m`);
1292
+ logger$10.log("");
1154
1293
  }
1155
1294
  }
1156
1295
  async restart() {
@@ -1236,7 +1375,6 @@ ${credentialsInjection}${parentWatch}const port = process.argv.includes('--port'
1236
1375
  const { app } = await import('${appImportPath}');
1237
1376
 
1238
1377
  ${serveCode.replace(/^ {4}/gm, "")}
1239
- console.log(\`Server started on port \${port}\`);
1240
1378
  `;
1241
1379
  return `#!/usr/bin/env node
1242
1380
  /**
@@ -1265,6 +1403,31 @@ start({
1265
1403
  });
1266
1404
  `;
1267
1405
  }
1406
+ /** Run `fn` with `console.log` muted — warnings and errors still print. */
1407
+ async function quietly(fn) {
1408
+ const log = console.log;
1409
+ console.log = () => {};
1410
+ try {
1411
+ return await fn();
1412
+ } finally {
1413
+ console.log = log;
1414
+ }
1415
+ }
1416
+ /**
1417
+ * How to run a TypeScript entry with the app's own tsx.
1418
+ *
1419
+ * Straight through node rather than `npx tsx`: npx reads the developer's npm
1420
+ * config on every start and warned about each key it did not recognise — nine
1421
+ * lines per app, per restart, about nothing gkm does.
1422
+ */
1423
+ function tsxCommand(appRoot, args) {
1424
+ try {
1425
+ const cli = createRequire(join(appRoot, "package.json")).resolve("tsx/cli");
1426
+ return [process.execPath, [cli, ...args]];
1427
+ } catch {
1428
+ return ["npx", ["tsx", ...args]];
1429
+ }
1430
+ }
1268
1431
  /** A port this app must serve on is held by something else. */
1269
1432
  var DevPortInUse = class extends Error {
1270
1433
  port;
@@ -1331,6 +1494,13 @@ var DevServer = class {
1331
1494
  this.selfServing = selfServing;
1332
1495
  this.actualPort = requestedPort;
1333
1496
  }
1497
+ /** What the ready line calls this app. */
1498
+ label = "server";
1499
+ /**
1500
+ * Where the app is reached — its HTTPS address behind the edge, when it has
1501
+ * one. The local port is only what the edge forwards to.
1502
+ */
1503
+ publicUrl;
1334
1504
  async start() {
1335
1505
  this.startTime = Date.now();
1336
1506
  if (this.isRunning) await this.stop();
@@ -1339,17 +1509,15 @@ var DevServer = class {
1339
1509
  this.actualPort = this.requestedPort;
1340
1510
  } else {
1341
1511
  this.actualPort = await findAvailablePort(this.requestedPort);
1342
- if (this.actualPort !== this.requestedPort) logger$11.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
1512
+ if (this.actualPort !== this.requestedPort) logger$10.log(`ℹ️ Port ${this.requestedPort} was in use, using port ${this.actualPort} instead`);
1343
1513
  }
1344
1514
  const serverEntryPath = join(this.appRoot, ".gkm", this.provider, "server.ts");
1345
1515
  await this.createServerEntry();
1346
- logger$11.log(`\n⏳ Starting server...`);
1347
- this.serverProcess = spawn("npx", [
1348
- "tsx",
1516
+ this.serverProcess = spawn(...tsxCommand(this.appRoot, [
1349
1517
  serverEntryPath,
1350
1518
  "--port",
1351
1519
  this.actualPort.toString()
1352
- ], {
1520
+ ]), {
1353
1521
  stdio: "inherit",
1354
1522
  env: {
1355
1523
  ...process.env,
@@ -1360,29 +1528,24 @@ var DevServer = class {
1360
1528
  });
1361
1529
  this.isRunning = true;
1362
1530
  this.serverProcess.on("error", (error) => {
1363
- logger$11.error("❌ Server error:", error);
1531
+ logger$10.error("❌ Server error:", error);
1364
1532
  });
1365
1533
  this.serverProcess.on("exit", (code, signal) => {
1366
- if (code !== null && code !== 0 && signal !== "SIGTERM") logger$11.error(`❌ Server exited with code ${code}`);
1534
+ if (code !== null && code !== 0 && signal !== "SIGTERM") logger$10.error(`❌ Server exited with code ${code}`);
1367
1535
  this.isRunning = false;
1368
1536
  });
1369
1537
  await new Promise((resolve) => setTimeout(resolve, 1e3));
1370
1538
  if (this.isRunning) {
1371
- const base = `http://localhost:${this.actualPort}`;
1372
- const lines = [` Local: ${base}`];
1373
- if (this.enableOpenApi) lines.push(` API Docs: ${base}/__docs`);
1374
- if (this.telescope) lines.push(` Telescope: ${base}${this.telescope.path}`);
1375
- if (this.studio) lines.push(` Studio: ${base}${this.studio.path}`);
1376
- const maxLen = Math.max(...lines.map((l) => l.length));
1377
- const pad = (s) => s.padEnd(maxLen);
1378
- const border = "─".repeat(maxLen + 2);
1379
- logger$11.log("");
1380
- logger$11.log(` \x1b[32m✓ Ready\x1b[0m in ${((Date.now() - this.startTime) / 1e3).toFixed(1)}s`);
1381
- logger$11.log("");
1382
- logger$11.log(` ┌${border}┐`);
1383
- for (const line of lines) logger$11.log(` │ ${pad(line)} │`);
1384
- logger$11.log(` └${border}┘`);
1385
- logger$11.log("");
1539
+ const local = `http://localhost:${this.actualPort}`;
1540
+ const address = this.publicUrl ? `${this.publicUrl} -> ${local}` : local;
1541
+ const seconds = ((Date.now() - this.startTime) / 1e3).toFixed(1);
1542
+ logger$10.log(`\x1b[32m✓\x1b[0m ${this.label} ready in ${seconds}s ${address}`);
1543
+ const tools = this.selfServing ? [] : [
1544
+ ...this.enableOpenApi ? ["docs /__docs"] : [],
1545
+ ...this.telescope ? [`telescope ${this.telescope.path}`] : [],
1546
+ ...this.studio ? [`studio ${this.studio.path}`] : []
1547
+ ];
1548
+ if (tools.length > 0) logger$10.log(` ${tools.join(" · ")}`);
1386
1549
  }
1387
1550
  }
1388
1551
  async stop() {
@@ -2197,7 +2360,7 @@ var TopicGenerator = class extends ConstructGenerator {
2197
2360
  };
2198
2361
  //#endregion
2199
2362
  //#region src/build/manifests.ts
2200
- const logger$10 = console;
2363
+ const logger$9 = console;
2201
2364
  function isPartitioned(field) {
2202
2365
  return !Array.isArray(field);
2203
2366
  }
@@ -2294,8 +2457,8 @@ export type RoutePath = Route['path'];
2294
2457
  `;
2295
2458
  const manifestPath = join(manifestDir, "aws.ts");
2296
2459
  await writeFile(manifestPath, content);
2297
- logger$10.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers, ${countItems(queues)} queues, ${countItems(topics)} topics`);
2298
- logger$10.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2460
+ logger$9.log(`Generated AWS manifest with ${countItems(awsRoutes)} routes, ${countItems(functions)} functions, ${countItems(crons)} crons, ${countItems(subscribers)} subscribers, ${countItems(queues)} queues, ${countItems(topics)} topics`);
2461
+ logger$9.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2299
2462
  }
2300
2463
  async function generateServerManifest(outputDir, appInfo, routes, subscribers, queues = [], topics = [], constructs = {}, backends = {}) {
2301
2464
  const manifestDir = join(outputDir, "manifest");
@@ -2350,8 +2513,8 @@ export type RoutePath = Route['path'];
2350
2513
  `;
2351
2514
  const manifestPath = join(manifestDir, "server.ts");
2352
2515
  await writeFile(manifestPath, content);
2353
- logger$10.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`);
2354
- logger$10.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2516
+ logger$9.log(`Generated server manifest with ${countItems(serverRoutes)} routes, ${countItems(serverSubscribers)} subscribers, ${countItems(serverQueues)} queues, ${countItems(serverTopics)} topics`);
2517
+ logger$9.log(`Manifest: ${relative(process.cwd(), manifestPath)}`);
2355
2518
  }
2356
2519
  /**
2357
2520
  * Filter out 'ALL' method routes from a manifest field (flat or partitioned).
@@ -2556,7 +2719,7 @@ export default app;
2556
2719
  }
2557
2720
  //#endregion
2558
2721
  //#region src/build/index.ts
2559
- const logger$9 = console;
2722
+ const logger$8 = console;
2560
2723
  /**
2561
2724
  * What a build run at the workspace root reads.
2562
2725
  *
@@ -2581,7 +2744,7 @@ async function buildCommand(options) {
2581
2744
  const isAtWorkspaceRoot = cwd === workspaceRoot;
2582
2745
  const someAppIsElsewhere = Object.values(loadedConfig.workspace.apps).some((app) => resolve(workspaceRoot, app.path) !== workspaceRoot);
2583
2746
  if (isAtWorkspaceRoot && someAppIsElsewhere) {
2584
- logger$9.log("📦 Detected workspace configuration");
2747
+ logger$8.log("📦 Detected workspace configuration");
2585
2748
  return workspaceBuildCommand(loadedConfig.workspace, options);
2586
2749
  }
2587
2750
  }
@@ -2590,15 +2753,15 @@ async function buildCommand(options) {
2590
2753
  const cacheBackend = cacheBackendFor(providerOf(loadedConfig.workspace));
2591
2754
  const productionConfigFromGkm = getProductionConfigFromGkm(config);
2592
2755
  const production = normalizeProductionConfig(options.production ?? false, productionConfigFromGkm);
2593
- if (production) logger$9.log(`🏭 Building for PRODUCTION`);
2594
- logger$9.log(`Building with providers: ${resolved.providers.join(", ")}`);
2595
- logger$9.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
2756
+ if (production) logger$8.log(`🏭 Building for PRODUCTION`);
2757
+ logger$8.log(`Building with providers: ${resolved.providers.join(", ")}`);
2758
+ logger$8.log(`Loading constructs from: ${formatRoutes(config.constructs)}`);
2596
2759
  const telescope = production ? void 0 : normalizeTelescopeConfig(config.telescope);
2597
- if (telescope) logger$9.log(`🔭 Telescope enabled at ${telescope.path}`);
2760
+ if (telescope) logger$8.log(`🔭 Telescope enabled at ${telescope.path}`);
2598
2761
  const studio = production ? void 0 : normalizeStudioConfig(config.studio);
2599
- if (studio) logger$9.log(`🗄️ Studio enabled at ${studio.path}`);
2762
+ if (studio) logger$8.log(`🗄️ Studio enabled at ${studio.path}`);
2600
2763
  const hooks = normalizeHooksConfig(config.hooks);
2601
- if (hooks) logger$9.log(`🪝 Server hooks enabled`);
2764
+ if (hooks) logger$8.log(`🪝 Server hooks enabled`);
2602
2765
  return buildApp({
2603
2766
  config,
2604
2767
  workspaceRoot: loadedConfig.workspace.root,
@@ -2641,7 +2804,7 @@ async function buildApp(input) {
2641
2804
  });
2642
2805
  if (selfServing) {
2643
2806
  await writeSurfaceEntry(join(appRoot, ".gkm", "server"), selfServing.source);
2644
- logger$9.log(`Generated a server for ${selfServing.id} from its own declaration`);
2807
+ logger$8.log(`Generated a server for ${selfServing.id} from its own declaration`);
2645
2808
  return { selfServing: selfServing.id };
2646
2809
  }
2647
2810
  const derived = ownersContext({
@@ -2681,14 +2844,14 @@ async function buildApp(input) {
2681
2844
  topicGenerator.load(code, appRoot, bustCache)
2682
2845
  ]);
2683
2846
  const allEndpoints = servedBy(loadedEndpoints, derived.surface);
2684
- logger$9.log(`Found ${allEndpoints.length} endpoints`);
2685
- logger$9.log(`Found ${allFunctions.length} functions`);
2686
- logger$9.log(`Found ${allCrons.length} crons`);
2687
- logger$9.log(`Found ${allSubscribers.length} subscribers`);
2688
- logger$9.log(`Found ${allQueues.length} queues`);
2689
- logger$9.log(`Found ${allTopics.length} topics`);
2847
+ logger$8.log(`Found ${allEndpoints.length} endpoints`);
2848
+ logger$8.log(`Found ${allFunctions.length} functions`);
2849
+ logger$8.log(`Found ${allCrons.length} crons`);
2850
+ logger$8.log(`Found ${allSubscribers.length} subscribers`);
2851
+ logger$8.log(`Found ${allQueues.length} queues`);
2852
+ logger$8.log(`Found ${allTopics.length} topics`);
2690
2853
  if (!input.serveEmpty && allEndpoints.length === 0 && allFunctions.length === 0 && allCrons.length === 0 && allSubscribers.length === 0 && allQueues.length === 0 && allTopics.length === 0) {
2691
- logger$9.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
2854
+ logger$8.log("No endpoints, functions, crons, subscribers, queues, or topics found to process");
2692
2855
  return {};
2693
2856
  }
2694
2857
  const rootOutputDir = join(appRoot, ".gkm");
@@ -2705,7 +2868,7 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2705
2868
  const rootOutputDir = join(appRoot, ".gkm");
2706
2869
  const outputDir = join(rootOutputDir, provider);
2707
2870
  await mkdir(outputDir, { recursive: true });
2708
- logger$9.log(`\nGenerating handlers for provider: ${provider}`);
2871
+ logger$8.log(`\nGenerating handlers for provider: ${provider}`);
2709
2872
  const [routes, functionInfos, cronInfos, subscriberInfos, queueInfos, topicInfos] = await Promise.all([
2710
2873
  endpointGenerator.build(context, endpoints, outputDir, {
2711
2874
  provider,
@@ -2717,7 +2880,7 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2717
2880
  queueGenerator.build(context, queues, outputDir, { provider }),
2718
2881
  topicGenerator.build(context, topics, outputDir, { provider })
2719
2882
  ]);
2720
- logger$9.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`);
2883
+ logger$8.log(`Generated ${routes.length} routes, ${functionInfos.length} functions, ${cronInfos.length} crons, ${subscriberInfos.length} subscribers, ${queueInfos.length} queues, ${topicInfos.length} topics for ${provider}`);
2721
2884
  const manifestRoutes = assembleManifestField(routes, endpoints);
2722
2885
  const manifestFunctions = assembleManifestField(functionInfos, functions);
2723
2886
  const manifestCrons = assembleManifestField(cronInfos, crons);
@@ -2737,8 +2900,8 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2737
2900
  }, serverRouteField, manifestSubscribers, manifestQueues, manifestTopics);
2738
2901
  let masterKey;
2739
2902
  if (context.production?.bundle && !skipBundle) {
2740
- logger$9.log(`\n📦 Bundling production server...`);
2741
- const { bundleServer } = await import("./bundler-mwH7WOb1.mjs");
2903
+ logger$8.log(`\n📦 Bundling production server...`);
2904
+ const { bundleServer } = await import("./bundler-D5RcGmVM.mjs");
2742
2905
  const allConstructs = [
2743
2906
  ...endpoints.map((e) => e.construct),
2744
2907
  ...functions.map((f) => f.construct),
@@ -2755,10 +2918,10 @@ async function buildForProvider(provider, context, appRoot, discovered, endpoint
2755
2918
  stage,
2756
2919
  constructs: allConstructs
2757
2920
  })).masterKey;
2758
- logger$9.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
2921
+ logger$8.log(`✅ Bundle complete: .gkm/server/dist/server.mjs`);
2759
2922
  if (masterKey) {
2760
- logger$9.log(`\n🔐 Secrets encrypted for deployment`);
2761
- logger$9.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
2923
+ logger$8.log(`\n🔐 Secrets encrypted for deployment`);
2924
+ logger$8.log(` Deploy with: GKM_MASTER_KEY=${masterKey}`);
2762
2925
  }
2763
2926
  }
2764
2927
  return { masterKey };
@@ -2845,19 +3008,19 @@ async function workspaceBuildCommand(workspace, options) {
2845
3008
  const apps = Object.entries(workspace.apps);
2846
3009
  const backendApps = apps.filter(([, app]) => app.type === "backend");
2847
3010
  const frontendApps = apps.filter(([, app]) => app.type === "web");
2848
- logger$9.log(`\n🏗️ Building workspace: ${workspace.name}`);
2849
- logger$9.log(` Backend apps: ${backendApps.map(([name]) => name).join(", ") || "none"}`);
2850
- logger$9.log(` Frontend apps: ${frontendApps.map(([name]) => name).join(", ") || "none"}`);
2851
- if (options.production) logger$9.log(` 🏭 Production mode enabled`);
3011
+ logger$8.log(`\n🏗️ Building workspace: ${workspace.name}`);
3012
+ logger$8.log(` Backend apps: ${backendApps.map(([name]) => name).join(", ") || "none"}`);
3013
+ logger$8.log(` Frontend apps: ${frontendApps.map(([name]) => name).join(", ") || "none"}`);
3014
+ if (options.production) logger$8.log(` 🏭 Production mode enabled`);
2852
3015
  const buildOrder = getAppBuildOrder(workspace);
2853
- logger$9.log(` Build order: ${buildOrder.join(" → ")}`);
3016
+ logger$8.log(` Build order: ${buildOrder.join(" → ")}`);
2854
3017
  const pm = detectPackageManager$2();
2855
- logger$9.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
3018
+ logger$8.log(`\n📦 Using ${pm} with Turbo for parallel builds...\n`);
2856
3019
  try {
2857
3020
  const { filters, unpackaged } = turboFilters(workspace);
2858
3021
  if (unpackaged.length > 0) throw new Error(`No package.json for workspace app(s): ${unpackaged.join(", ")}. Each app needs one — turbo builds packages, and gkm reads the name back to know which app it is building.`);
2859
3022
  const turboCommand = getTurboCommand(pm, filters);
2860
- logger$9.log(`Running: ${turboCommand}`);
3023
+ logger$8.log(`Running: ${turboCommand}`);
2861
3024
  await new Promise((resolve, reject) => {
2862
3025
  const child = spawn(turboCommand, {
2863
3026
  shell: true,
@@ -2885,15 +3048,15 @@ async function workspaceBuildCommand(workspace, options) {
2885
3048
  outputPath
2886
3049
  });
2887
3050
  }
2888
- logger$9.log(`\n✅ Workspace build complete!`);
2889
- logger$9.log(`\n📋 Build Summary:`);
3051
+ logger$8.log(`\n✅ Workspace build complete!`);
3052
+ logger$8.log(`\n📋 Build Summary:`);
2890
3053
  for (const result of results) {
2891
3054
  const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
2892
- logger$9.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
3055
+ logger$8.log(` ${icon} ${result.appName}: ${result.outputPath || "built"}`);
2893
3056
  }
2894
3057
  } catch (error) {
2895
3058
  const errorMessage = error instanceof Error ? error.message : "Build failed";
2896
- logger$9.log(`\n❌ Build failed: ${errorMessage}`);
3059
+ logger$8.log(`\n❌ Build failed: ${errorMessage}`);
2897
3060
  for (const [appName, app] of apps) results.push({
2898
3061
  appName,
2899
3062
  type: app.type,
@@ -3749,7 +3912,7 @@ async function createDnsProvider(options) {
3749
3912
  if (isDnsProvider(config.provider)) return config.provider;
3750
3913
  const provider = config.provider;
3751
3914
  if (provider === "hostinger") {
3752
- const { HostingerProvider } = await import("./HostingerProvider-BY26bWwB.mjs");
3915
+ const { HostingerProvider } = await import("./HostingerProvider-Btxpto71.mjs");
3753
3916
  return new HostingerProvider();
3754
3917
  }
3755
3918
  if (provider === "route53") {
@@ -3771,7 +3934,7 @@ async function createDnsProvider(options) {
3771
3934
  *
3772
3935
  * Handles automatic DNS record creation for deployed applications.
3773
3936
  */
3774
- const logger$8 = console;
3937
+ const logger$7 = console;
3775
3938
  /**
3776
3939
  * Check if DNS config is legacy format (single domain with `domain` property)
3777
3940
  */
@@ -3808,7 +3971,7 @@ function groupHostnamesByDomain(appHostnames, dnsConfig) {
3808
3971
  for (const [appName, hostname] of appHostnames) {
3809
3972
  const rootDomain = findRootDomain(hostname, dnsConfig);
3810
3973
  if (!rootDomain) {
3811
- logger$8.log(` ⚠ No DNS config found for hostname: ${hostname}`);
3974
+ logger$7.log(` ⚠ No DNS config found for hostname: ${hostname}`);
3812
3975
  continue;
3813
3976
  }
3814
3977
  if (!grouped.has(rootDomain)) grouped.set(rootDomain, /* @__PURE__ */ new Map());
@@ -3858,10 +4021,10 @@ function generateRequiredRecords(appHostnames, rootDomain, serverIp) {
3858
4021
  * Print DNS records table
3859
4022
  */
3860
4023
  function printDnsRecordsTable(records, rootDomain) {
3861
- logger$8.log(`\n 📋 DNS Records for ${rootDomain}:`);
3862
- logger$8.log(" ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐");
3863
- logger$8.log(" │ Subdomain │ Type │ Value │ Status │");
3864
- logger$8.log(" ├─────────────────────────────────────┼──────┼─────────────────┼────────┤");
4024
+ logger$7.log(`\n 📋 DNS Records for ${rootDomain}:`);
4025
+ logger$7.log(" ┌─────────────────────────────────────┬──────┬─────────────────┬────────┐");
4026
+ logger$7.log(" │ Subdomain │ Type │ Value │ Status │");
4027
+ logger$7.log(" ├─────────────────────────────────────┼──────┼─────────────────┼────────┤");
3865
4028
  for (const record of records) {
3866
4029
  const subdomain = record.subdomain.padEnd(35);
3867
4030
  const type = record.type.padEnd(4);
@@ -3871,18 +4034,18 @@ function printDnsRecordsTable(records, rootDomain) {
3871
4034
  else if (record.created) status = "✓ new";
3872
4035
  else if (record.existed) status = "✓";
3873
4036
  else status = "?";
3874
- logger$8.log(` │ ${subdomain} │ ${type} │ ${value} │ ${status.padEnd(6)} │`);
4037
+ logger$7.log(` │ ${subdomain} │ ${type} │ ${value} │ ${status.padEnd(6)} │`);
3875
4038
  }
3876
- logger$8.log(" └─────────────────────────────────────┴──────┴─────────────────┴────────┘");
4039
+ logger$7.log(" └─────────────────────────────────────┴──────┴─────────────────┴────────┘");
3877
4040
  }
3878
4041
  /**
3879
4042
  * Print DNS records in a simple format for manual setup
3880
4043
  */
3881
4044
  function printDnsRecordsSimple(records, rootDomain) {
3882
- logger$8.log("\n 📋 Required DNS Records:");
3883
- logger$8.log(` Add these A records to your DNS provider (${rootDomain}):\n`);
3884
- for (const record of records) logger$8.log(` ${record.subdomain} → ${record.value} (A record)`);
3885
- logger$8.log("");
4045
+ logger$7.log("\n 📋 Required DNS Records:");
4046
+ logger$7.log(` Add these A records to your DNS provider (${rootDomain}):\n`);
4047
+ for (const record of records) logger$7.log(` ${record.subdomain} → ${record.value} (A record)`);
4048
+ logger$7.log("");
3886
4049
  }
3887
4050
  /**
3888
4051
  * Create DNS records for a single domain using its configured provider
@@ -3894,7 +4057,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
3894
4057
  provider = await createDnsProvider({ config: providerConfig });
3895
4058
  } catch (error) {
3896
4059
  const message = error instanceof Error ? error.message : "Unknown error";
3897
- logger$8.log(` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`);
4060
+ logger$7.log(` ⚠ Failed to create DNS provider for ${rootDomain}: ${message}`);
3898
4061
  return records.map((r) => ({
3899
4062
  ...r,
3900
4063
  error: message
@@ -3948,7 +4111,7 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
3948
4111
  }
3949
4112
  } catch (error) {
3950
4113
  const message = error instanceof Error ? error.message : "Unknown error";
3951
- logger$8.log(` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`);
4114
+ logger$7.log(` ⚠ Failed to create DNS records for ${rootDomain}: ${message}`);
3952
4115
  return records.map((r) => ({
3953
4116
  hostname: r.hostname,
3954
4117
  subdomain: r.subdomain,
@@ -3975,20 +4138,20 @@ async function createDnsRecordsForDomain(records, rootDomain, providerConfig) {
3975
4138
  async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
3976
4139
  if (!dnsConfig) return null;
3977
4140
  const normalizedConfig = normalizeDnsConfig(dnsConfig);
3978
- logger$8.log("\n🌐 Setting up DNS records...");
4141
+ logger$7.log("\n🌐 Setting up DNS records...");
3979
4142
  let serverIp;
3980
4143
  try {
3981
4144
  const endpointUrl = new URL(dokployEndpoint);
3982
4145
  serverIp = await resolveHostnameToIp(endpointUrl.hostname);
3983
- logger$8.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);
4146
+ logger$7.log(` Server IP: ${serverIp} (from ${endpointUrl.hostname})`);
3984
4147
  } catch (error) {
3985
4148
  const message = error instanceof Error ? error.message : "Unknown error";
3986
- logger$8.log(` ⚠ Failed to resolve server IP: ${message}`);
4149
+ logger$7.log(` ⚠ Failed to resolve server IP: ${message}`);
3987
4150
  return null;
3988
4151
  }
3989
4152
  const groupedHostnames = groupHostnamesByDomain(appHostnames, normalizedConfig);
3990
4153
  if (groupedHostnames.size === 0) {
3991
- logger$8.log(" No DNS records needed (no hostnames match configured domains)");
4154
+ logger$7.log(" No DNS records needed (no hostnames match configured domains)");
3992
4155
  return {
3993
4156
  records: [],
3994
4157
  success: true,
@@ -4000,22 +4163,22 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
4000
4163
  for (const [rootDomain, domainHostnames] of groupedHostnames) {
4001
4164
  const providerConfig = normalizedConfig[rootDomain];
4002
4165
  if (!providerConfig) {
4003
- logger$8.log(` ⚠ No provider config for ${rootDomain}`);
4166
+ logger$7.log(` ⚠ No provider config for ${rootDomain}`);
4004
4167
  continue;
4005
4168
  }
4006
4169
  const providerName = typeof providerConfig.provider === "string" ? providerConfig.provider : "custom";
4007
4170
  const requiredRecords = generateRequiredRecords(domainHostnames, rootDomain, serverIp);
4008
4171
  if (requiredRecords.length === 0) continue;
4009
- logger$8.log(` Creating DNS records for ${rootDomain} (${providerName})...`);
4172
+ logger$7.log(` Creating DNS records for ${rootDomain} (${providerName})...`);
4010
4173
  const domainRecords = await createDnsRecordsForDomain(requiredRecords, rootDomain, providerConfig);
4011
4174
  allRecords.push(...domainRecords);
4012
4175
  const created = domainRecords.filter((r) => r.created).length;
4013
4176
  const existed = domainRecords.filter((r) => r.existed).length;
4014
4177
  const failed = domainRecords.filter((r) => r.error).length;
4015
- if (created > 0) logger$8.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);
4016
- if (existed > 0) logger$8.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);
4178
+ if (created > 0) logger$7.log(` ✓ Created ${created} DNS record(s) for ${rootDomain}`);
4179
+ if (existed > 0) logger$7.log(` ✓ ${existed} record(s) already exist for ${rootDomain}`);
4017
4180
  if (failed > 0) {
4018
- logger$8.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
4181
+ logger$7.log(` ⚠ ${failed} record(s) failed for ${rootDomain}`);
4019
4182
  hasFailures = true;
4020
4183
  }
4021
4184
  if (state) {
@@ -4052,10 +4215,10 @@ async function orchestrateDns(appHostnames, dnsConfig, dokployEndpoint, state) {
4052
4215
  */
4053
4216
  async function verifyDnsRecords(appHostnames, serverIp, state) {
4054
4217
  const results = [];
4055
- logger$8.log("\n🔍 Verifying DNS records...");
4218
+ logger$7.log("\n🔍 Verifying DNS records...");
4056
4219
  for (const [appName, hostname] of appHostnames) {
4057
4220
  if (isDnsVerified(state, hostname, serverIp)) {
4058
- logger$8.log(` ✓ ${hostname} (previously verified)`);
4221
+ logger$7.log(` ✓ ${hostname} (previously verified)`);
4059
4222
  results.push({
4060
4223
  hostname,
4061
4224
  appName,
@@ -4069,7 +4232,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
4069
4232
  const resolvedIp = await resolveHostnameToIp(hostname);
4070
4233
  if (resolvedIp === serverIp) {
4071
4234
  setDnsVerification(state, hostname, serverIp);
4072
- logger$8.log(` ✓ ${hostname} → ${resolvedIp}`);
4235
+ logger$7.log(` ✓ ${hostname} → ${resolvedIp}`);
4073
4236
  results.push({
4074
4237
  hostname,
4075
4238
  appName,
@@ -4078,7 +4241,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
4078
4241
  expectedIp: serverIp
4079
4242
  });
4080
4243
  } else {
4081
- logger$8.log(` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`);
4244
+ logger$7.log(` ⚠ ${hostname} resolves to ${resolvedIp}, expected ${serverIp}`);
4082
4245
  results.push({
4083
4246
  hostname,
4084
4247
  appName,
@@ -4089,7 +4252,7 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
4089
4252
  }
4090
4253
  } catch (error) {
4091
4254
  const message = error instanceof Error ? error.message : "Unknown error";
4092
- logger$8.log(` ⚠ ${hostname} DNS not propagated (${message})`);
4255
+ logger$7.log(` ⚠ ${hostname} DNS not propagated (${message})`);
4093
4256
  results.push({
4094
4257
  hostname,
4095
4258
  appName,
@@ -4103,9 +4266,9 @@ async function verifyDnsRecords(appHostnames, serverIp, state) {
4103
4266
  const skipped = results.filter((r) => r.skipped).length;
4104
4267
  const pending = results.filter((r) => !r.verified).length;
4105
4268
  if (pending > 0) {
4106
- logger$8.log(`\n ${verified} verified, ${pending} pending propagation`);
4107
- logger$8.log(" DNS changes may take 5-30 minutes to propagate");
4108
- } else if (skipped > 0) logger$8.log(` ${verified} verified (${skipped} from cache)`);
4269
+ logger$7.log(`\n ${verified} verified, ${pending} pending propagation`);
4270
+ logger$7.log(" DNS changes may take 5-30 minutes to propagate");
4271
+ } else if (skipped > 0) logger$7.log(` ${verified} verified (${skipped} from cache)`);
4109
4272
  return results;
4110
4273
  }
4111
4274
  //#endregion
@@ -4138,43 +4301,19 @@ function resolveHost(appName, app, stage, dokployConfig, isMainFrontend) {
4138
4301
  return `${appName}.${baseDomain}`;
4139
4302
  }
4140
4303
  /**
4141
- * Which site the base domain points at.
4142
- *
4143
- * Three rules, in order, and the last one is the point:
4144
- *
4145
- * 1. **One site** — it is the root. Unambiguous, so nothing has to be said.
4146
- * 2. **Named `web`** — the convention wins, because people already rely on it.
4147
- * 3. **Declared `root: true`** — the site says so itself.
4148
- *
4149
- * Anything else throws. It used to take the *first* `type: 'web'` it iterated,
4150
- * and that object is built from the manifest, which is built in glob traversal
4151
- * order — so two sites with neither named `web` meant renaming a file could
4152
- * move the production root domain, silently. The same shape as
4153
- * `CacheIsAmbiguous`: unambiguous with one and arbitrary with two, so two is an
4154
- * error rather than a coin toss.
4304
+ * Whether this app is the site the base domain points at — see `rootSite`,
4305
+ * which the local edge asks too.
4155
4306
  *
4156
4307
  * @throws {AmbiguousRootSite} when several sites could be the root and none says it is
4157
4308
  */
4158
4309
  function isMainFrontendApp(appName, app, allApps) {
4159
4310
  if (app.type !== "web") return false;
4160
- const sites = Object.entries(allApps).filter(([, a]) => a.type === "web");
4161
- if (sites.length === 1) return true;
4162
- if (sites.filter(([name]) => name === "web").length > 0) return appName === "web";
4163
- const declared = sites.filter(([, a]) => a.root === true);
4164
- if (declared.length === 1) return declared[0][0] === appName;
4165
- throw new AmbiguousRootSite(sites.map(([name]) => name), declared.length > 1);
4166
- }
4167
- /** Several sites could hold the base domain, and none of them says it does. */
4168
- var AmbiguousRootSite = class extends Error {
4169
- sites;
4170
- tooMany;
4171
- constructor(sites, tooMany = false) {
4172
- super(tooMany ? `More than one site declares \`root: true\` — ${sites.join(", ")}. The base domain points at one of them.` : `${sites.length} sites and nothing says which holds the base domain: ${sites.join(", ")}. Name one of them \`web\`, or declare \`root: true\` on the one the base domain points at. It used to be whichever the glob reached first, which is not a thing to decide a production hostname.`);
4173
- this.sites = sites;
4174
- this.tooMany = tooMany;
4175
- this.name = "AmbiguousRootSite";
4176
- }
4177
- };
4311
+ const sites = Object.entries(allApps).filter(([, a]) => a.type === "web").map(([name, a]) => ({
4312
+ name,
4313
+ root: a.root
4314
+ }));
4315
+ return rootSite(sites) === appName;
4316
+ }
4178
4317
  /**
4179
4318
  * Get public URL arg names from app dependencies.
4180
4319
  *
@@ -5037,7 +5176,7 @@ CMD ["nginx", "-g", "daemon off;"]
5037
5176
  }
5038
5177
  //#endregion
5039
5178
  //#region src/docker/index.ts
5040
- const logger$7 = console;
5179
+ const logger$6 = console;
5041
5180
  /**
5042
5181
  * Docker command implementation
5043
5182
  * Generates Dockerfile, docker-compose.yml, and related files
@@ -5048,7 +5187,7 @@ const logger$7 = console;
5048
5187
  async function dockerCommand(options) {
5049
5188
  const loadedConfig = await loadWorkspaceConfig();
5050
5189
  if (loadedConfig.type === "workspace") {
5051
- logger$7.log("📦 Detected workspace configuration");
5190
+ logger$6.log("📦 Detected workspace configuration");
5052
5191
  return workspaceDockerCommand(loadedConfig.workspace);
5053
5192
  }
5054
5193
  const config = await loadConfig();
@@ -5068,7 +5207,7 @@ async function dockerCommand(options) {
5068
5207
  if (inMonorepo && !useSlim) {
5069
5208
  if (hasTurbo) {
5070
5209
  useTurbo = true;
5071
- logger$7.log(" Detected monorepo with turbo.json - using turbo prune");
5210
+ logger$6.log(" Detected monorepo with turbo.json - using turbo prune");
5072
5211
  } else throw new Error("Monorepo detected but turbo.json not found.\n\nDocker builds in monorepos require Turborepo for proper dependency isolation.\n\nTo fix this:\n 1. Install turbo: pnpm add -Dw turbo\n 2. Create turbo.json in your monorepo root\n 3. Run this command again\n\nSee: https://turbo.build/repo/docs/guides/tools/docker");
5073
5212
  }
5074
5213
  let turboPackage = options.turboPackage ?? dockerConfig.imageName;
@@ -5076,7 +5215,7 @@ async function dockerCommand(options) {
5076
5215
  const pkg = __require(`${process.cwd()}/package.json`);
5077
5216
  if (pkg.name) {
5078
5217
  turboPackage = pkg.name;
5079
- logger$7.log(` Turbo package: ${turboPackage}`);
5218
+ logger$6.log(` Turbo package: ${turboPackage}`);
5080
5219
  }
5081
5220
  } catch {}
5082
5221
  const templateOptions = {
@@ -5093,16 +5232,16 @@ async function dockerCommand(options) {
5093
5232
  const dockerMode = useSlim ? "slim" : useTurbo ? "turbo" : "multi-stage";
5094
5233
  const dockerfilePath = join(dockerDir, "Dockerfile");
5095
5234
  await writeFile(dockerfilePath, dockerfile);
5096
- logger$7.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
5235
+ logger$6.log(`Generated: .gkm/docker/Dockerfile (${dockerMode}, ${packageManager})`);
5097
5236
  const composePath = await writeConstructsCompose(loadedConfig.workspace);
5098
5237
  const dockerignore = generateDockerignore();
5099
5238
  const dockerignorePath = join(process.cwd(), ".dockerignore");
5100
5239
  await writeFile(dockerignorePath, dockerignore);
5101
- logger$7.log("Generated: .dockerignore (project root)");
5240
+ logger$6.log("Generated: .dockerignore (project root)");
5102
5241
  const entrypoint = generateDockerEntrypoint();
5103
5242
  const entrypointPath = join(dockerDir, "docker-entrypoint.sh");
5104
5243
  await writeFile(entrypointPath, entrypoint);
5105
- logger$7.log("Generated: .gkm/docker/docker-entrypoint.sh");
5244
+ logger$6.log("Generated: .gkm/docker/docker-entrypoint.sh");
5106
5245
  const result = {
5107
5246
  dockerfile: dockerfilePath,
5108
5247
  dockerCompose: composePath,
@@ -5121,13 +5260,13 @@ async function dockerCommand(options) {
5121
5260
  function ensureLockfile(cwd) {
5122
5261
  const lockfilePath = findLockfilePath(cwd);
5123
5262
  if (!lockfilePath) {
5124
- logger$7.warn("\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.");
5263
+ logger$6.warn("\n⚠️ No lockfile found. Docker build may fail or use stale dependencies.");
5125
5264
  return null;
5126
5265
  }
5127
5266
  const lockfileName = basename(lockfilePath);
5128
5267
  const localLockfile = join(cwd, lockfileName);
5129
5268
  if (lockfilePath === localLockfile) return null;
5130
- logger$7.log(` Copying ${lockfileName} from monorepo root...`);
5269
+ logger$6.log(` Copying ${lockfileName} from monorepo root...`);
5131
5270
  copyFileSync(lockfilePath, localLockfile);
5132
5271
  return () => {
5133
5272
  try {
@@ -5143,7 +5282,7 @@ async function buildDockerImage(imageName, options) {
5143
5282
  const tag = options.tag ?? "latest";
5144
5283
  const registry = options.registry;
5145
5284
  const fullImageName = registry ? `${registry}/${imageName}:${tag}` : `${imageName}:${tag}`;
5146
- logger$7.log(`\n🐳 Building Docker image: ${fullImageName}`);
5285
+ logger$6.log(`\n🐳 Building Docker image: ${fullImageName}`);
5147
5286
  const cwd = process.cwd();
5148
5287
  const cleanup = ensureLockfile(cwd);
5149
5288
  try {
@@ -5155,7 +5294,7 @@ async function buildDockerImage(imageName, options) {
5155
5294
  DOCKER_BUILDKIT: "1"
5156
5295
  }
5157
5296
  });
5158
- logger$7.log(`✅ Docker image built: ${fullImageName}`);
5297
+ logger$6.log(`✅ Docker image built: ${fullImageName}`);
5159
5298
  } catch (error) {
5160
5299
  throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
5161
5300
  } finally {
@@ -5170,13 +5309,13 @@ async function pushDockerImage(imageName, options) {
5170
5309
  const registry = options.registry;
5171
5310
  if (!registry) throw new Error("Registry is required to push Docker image. Use --registry or configure docker.registry in gkm.config.ts");
5172
5311
  const fullImageName = `${registry}/${imageName}:${tag}`;
5173
- logger$7.log(`\n🚀 Pushing Docker image: ${fullImageName}`);
5312
+ logger$6.log(`\n🚀 Pushing Docker image: ${fullImageName}`);
5174
5313
  try {
5175
5314
  execSync(`docker push ${fullImageName}`, {
5176
5315
  cwd: process.cwd(),
5177
5316
  stdio: "inherit"
5178
5317
  });
5179
- logger$7.log(`✅ Docker image pushed: ${fullImageName}`);
5318
+ logger$6.log(`✅ Docker image pushed: ${fullImageName}`);
5180
5319
  } catch (error) {
5181
5320
  throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
5182
5321
  }
@@ -5201,21 +5340,21 @@ function getAppPackageName(appPath) {
5201
5340
  async function workspaceDockerCommand(workspace) {
5202
5341
  const results = [];
5203
5342
  const apps = Object.entries(workspace.apps);
5204
- logger$7.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
5343
+ logger$6.log(`\n🐳 Generating Dockerfiles for workspace: ${workspace.name}`);
5205
5344
  const dockerDir = join(workspace.root, ".gkm", "docker");
5206
5345
  await mkdir(dockerDir, { recursive: true });
5207
5346
  const packageManager = detectPackageManager$1(workspace.root);
5208
- logger$7.log(` Package manager: ${packageManager}`);
5347
+ logger$6.log(` Package manager: ${packageManager}`);
5209
5348
  for (const [appName, app] of apps) {
5210
5349
  if (app.type === "mobile") {
5211
- logger$7.log(`\n 📱 Skipping Docker for ${appName} (mobile app — deploy via framework toolchain)`);
5350
+ logger$6.log(`\n 📱 Skipping Docker for ${appName} (mobile app — deploy via framework toolchain)`);
5212
5351
  continue;
5213
5352
  }
5214
5353
  const appPath = app.path;
5215
5354
  const turboPackage = getAppPackageName(join(workspace.root, appPath)) ?? appName;
5216
5355
  const imageName = appName;
5217
5356
  const buildType = !!app.entry ? "entry" : app.type;
5218
- logger$7.log(`\n 📄 Generating Dockerfile for ${appName} (${buildType})`);
5357
+ logger$6.log(`\n 📄 Generating Dockerfile for ${appName} (${buildType})`);
5219
5358
  let dockerfile;
5220
5359
  if (app.type === "web") {
5221
5360
  const publicUrlArgs = getPublicUrlArgNames(app);
@@ -5259,7 +5398,7 @@ async function workspaceDockerCommand(workspace) {
5259
5398
  });
5260
5399
  const dockerfilePath = join(dockerDir, `Dockerfile.${appName}`);
5261
5400
  await writeFile(dockerfilePath, dockerfile);
5262
- logger$7.log(` Generated: .gkm/docker/Dockerfile.${appName}`);
5401
+ logger$6.log(` Generated: .gkm/docker/Dockerfile.${appName}`);
5263
5402
  results.push({
5264
5403
  appName,
5265
5404
  type: app.type,
@@ -5270,13 +5409,13 @@ async function workspaceDockerCommand(workspace) {
5270
5409
  const dockerignore = generateDockerignore();
5271
5410
  const dockerignorePath = join(workspace.root, ".dockerignore");
5272
5411
  await writeFile(dockerignorePath, dockerignore);
5273
- logger$7.log(`\n Generated: .dockerignore (workspace root)`);
5412
+ logger$6.log(`\n Generated: .dockerignore (workspace root)`);
5274
5413
  const composePath = await writeConstructsCompose(workspace);
5275
- logger$7.log(`\n✅ Generated ${results.length} Dockerfile(s) + ${COMPOSE_PATH}`);
5276
- logger$7.log("\n📋 Build commands:");
5414
+ logger$6.log(`\n✅ Generated ${results.length} Dockerfile(s) + ${COMPOSE_PATH}`);
5415
+ logger$6.log("\n📋 Build commands:");
5277
5416
  for (const result of results) {
5278
5417
  const icon = result.type === "backend" ? "⚙️" : result.type === "mobile" ? "📱" : "🌐";
5279
- logger$7.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
5418
+ logger$6.log(` ${icon} docker build -f .gkm/docker/Dockerfile.${result.appName} -t ${result.imageName} .`);
5280
5419
  }
5281
5420
  printRunInstructions(workspace);
5282
5421
  return {
@@ -5297,18 +5436,18 @@ async function writeConstructsCompose(workspace) {
5297
5436
  stage: workspace.stages.local,
5298
5437
  start: false
5299
5438
  });
5300
- logger$7.log(` Generated: ${COMPOSE_PATH} (project root)`);
5439
+ logger$6.log(` Generated: ${COMPOSE_PATH} (project root)`);
5301
5440
  return join(workspace.root, COMPOSE_PATH);
5302
5441
  }
5303
5442
  function printRunInstructions(workspace) {
5304
5443
  const project = existsSync(join(workspace.root, "docker-compose.yml")) ? " -f docker-compose.yml" : "";
5305
- logger$7.log("\n📋 Run everything (the apps sit behind the \"apps\" profile):");
5306
- logger$7.log(" gkm setup # starts the containers and creates what they hold");
5307
- logger$7.log(` docker compose -f ${COMPOSE_PATH}${project} --profile apps up --build`);
5444
+ logger$6.log("\n📋 Run everything (the apps sit behind the \"apps\" profile):");
5445
+ logger$6.log(" gkm setup # starts the containers and creates what they hold");
5446
+ logger$6.log(` docker compose -f ${COMPOSE_PATH}${project} --profile apps up --build`);
5308
5447
  }
5309
5448
  //#endregion
5310
5449
  //#region src/deploy/docker.ts
5311
- const logger$6 = console;
5450
+ const logger$5 = console;
5312
5451
  /**
5313
5452
  * Get the full image reference
5314
5453
  */
@@ -5323,9 +5462,9 @@ function getImageRef(registry, imageName, tag) {
5323
5462
  * @param buildArgs - Build arguments to pass to docker build
5324
5463
  */
5325
5464
  async function buildImage(imageRef, _appName, buildArgs) {
5326
- logger$6.log(`\n🔨 Building Docker image: ${imageRef}`);
5465
+ logger$5.log(`\n🔨 Building Docker image: ${imageRef}`);
5327
5466
  const cwd = process.cwd();
5328
- logger$6.log(" Generating Dockerfile for the pre-built bundle...");
5467
+ logger$5.log(" Generating Dockerfile for the pre-built bundle...");
5329
5468
  await dockerCommand({ slim: true });
5330
5469
  const dockerfilePath = join(cwd, ".gkm", "docker", "Dockerfile");
5331
5470
  const buildCwd = cwd;
@@ -5347,7 +5486,7 @@ async function buildImage(imageRef, _appName, buildArgs) {
5347
5486
  DOCKER_BUILDKIT: "1"
5348
5487
  }
5349
5488
  });
5350
- logger$6.log(`✅ Image built: ${imageRef}`);
5489
+ logger$5.log(`✅ Image built: ${imageRef}`);
5351
5490
  } catch (error) {
5352
5491
  throw new Error(`Failed to build Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
5353
5492
  }
@@ -5356,13 +5495,13 @@ async function buildImage(imageRef, _appName, buildArgs) {
5356
5495
  * Push Docker image to registry
5357
5496
  */
5358
5497
  async function pushImage(imageRef) {
5359
- logger$6.log(`\n☁️ Pushing image: ${imageRef}`);
5498
+ logger$5.log(`\n☁️ Pushing image: ${imageRef}`);
5360
5499
  try {
5361
5500
  execSync(`docker push ${imageRef}`, {
5362
5501
  cwd: process.cwd(),
5363
5502
  stdio: "inherit"
5364
5503
  });
5365
- logger$6.log(`✅ Image pushed: ${imageRef}`);
5504
+ logger$5.log(`✅ Image pushed: ${imageRef}`);
5366
5505
  } catch (error) {
5367
5506
  throw new Error(`Failed to push Docker image: ${error instanceof Error ? error.message : "Unknown error"}`);
5368
5507
  }
@@ -5376,18 +5515,18 @@ async function deployDocker(options) {
5376
5515
  const imageRef = getImageRef(config.registry, imageName, tag);
5377
5516
  await buildImage(imageRef, config.appName, buildArgs);
5378
5517
  if (!skipPush) {
5379
- if (!config.registry) logger$6.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
5518
+ if (!config.registry) logger$5.warn("\n⚠️ No registry configured. Use --skip-push or configure docker.registry in gkm.config.ts");
5380
5519
  else await pushImage(imageRef);
5381
5520
  }
5382
- logger$6.log("\n✅ Docker deployment ready!");
5383
- logger$6.log(`\n📋 Deployment details:`);
5384
- logger$6.log(` Image: ${imageRef}`);
5385
- logger$6.log(` Stage: ${stage}`);
5521
+ logger$5.log("\n✅ Docker deployment ready!");
5522
+ logger$5.log(`\n📋 Deployment details:`);
5523
+ logger$5.log(` Image: ${imageRef}`);
5524
+ logger$5.log(` Stage: ${stage}`);
5386
5525
  if (masterKey) {
5387
- logger$6.log(`\n🔐 Deploy with this environment variable:`);
5388
- logger$6.log(` GKM_MASTER_KEY=${masterKey}`);
5389
- logger$6.log("\n Example docker run:");
5390
- logger$6.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);
5526
+ logger$5.log(`\n🔐 Deploy with this environment variable:`);
5527
+ logger$5.log(` GKM_MASTER_KEY=${masterKey}`);
5528
+ logger$5.log("\n Example docker run:");
5529
+ logger$5.log(` docker run -e GKM_MASTER_KEY=${masterKey} ${imageRef}`);
5391
5530
  }
5392
5531
  return {
5393
5532
  imageRef,
@@ -6766,7 +6905,7 @@ async function sniffAllApps(apps, workspacePath, options = {}) {
6766
6905
  *
6767
6906
  * @module deploy
6768
6907
  */
6769
- const logger$5 = console;
6908
+ const logger$4 = console;
6770
6909
  /**
6771
6910
  * Prompt for input
6772
6911
  */
@@ -6840,7 +6979,7 @@ async function waitForPostgres(host, port, user, password, database, maxRetries
6840
6979
  return;
6841
6980
  } catch {
6842
6981
  if (i < maxRetries - 1) {
6843
- logger$5.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);
6982
+ logger$4.log(` Waiting for Postgres... (${i + 1}/${maxRetries})`);
6844
6983
  await new Promise((r) => setTimeout(r, retryIntervalMs));
6845
6984
  }
6846
6985
  }
@@ -6883,10 +7022,10 @@ async function applyDeclaredStatements(api, postgres, serverHostname, statements
6883
7022
  break;
6884
7023
  } catch (error) {
6885
7024
  if (!(error instanceof Error ? error.message : String(error)).includes("already in use")) throw error;
6886
- logger$5.log(` Port ${candidate} is taken; trying another...`);
7025
+ logger$4.log(` Port ${candidate} is taken; trying another...`);
6887
7026
  }
6888
7027
  if (externalPort === void 0) throw new Error(`Could not publish a port for ${postgres.appName}. In use on this server: ${[...taken].sort((a, b) => a - b).join(", ") || "none reported"}. The role DDL needs to reach the cluster from here.`);
6889
- logger$5.log(` Publishing ${postgres.appName} on ${externalPort}...`);
7028
+ logger$4.log(` Publishing ${postgres.appName} on ${externalPort}...`);
6890
7029
  }
6891
7030
  await api.deployPostgres(postgres.postgresId);
6892
7031
  await waitForPostgres(serverHostname, externalPort, postgres.databaseUser, postgres.databasePassword, postgres.databaseName);
@@ -6910,7 +7049,7 @@ async function applyDeclaredStatements(api, postgres, serverHostname, statements
6910
7049
  const unpublish = async () => {
6911
7050
  if (!opened) return;
6912
7051
  await api.savePostgresExternalPort(postgres.postgresId, null).then(() => api.deployPostgres(postgres.postgresId)).catch(() => {
6913
- logger$5.log(` ⚠ Could not close external port ${externalPort} on ${postgres.appName} — close it in Dokploy.`);
7052
+ logger$4.log(` ⚠ Could not close external port ${externalPort} on ${postgres.appName} — close it in Dokploy.`);
6914
7053
  });
6915
7054
  };
6916
7055
  let lastError;
@@ -6921,7 +7060,7 @@ async function applyDeclaredStatements(api, postgres, serverHostname, statements
6921
7060
  lastError = error;
6922
7061
  if (attempt === 3) break;
6923
7062
  const message = error instanceof Error ? error.message : String(error);
6924
- logger$5.log(` ⏳ Cluster still settling (${message}); retrying (${attempt}/2)...`);
7063
+ logger$4.log(` ⏳ Cluster still settling (${message}); retrying (${attempt}/2)...`);
6925
7064
  await new Promise((resolve) => setTimeout(resolve, 1e4));
6926
7065
  await waitForPostgres(serverHostname, externalPort, postgres.databaseUser, postgres.databasePassword, postgres.databaseName).catch(() => {});
6927
7066
  }
@@ -6996,91 +7135,91 @@ async function workspaceDeployCommand(configured, options) {
6996
7135
  apps: units
6997
7136
  } : configured;
6998
7137
  if (provider !== "dokploy") throw new Error(`Workspace deployment only supports Dokploy. Got: ${provider}`);
6999
- logger$5.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
7000
- logger$5.log(` Stage: ${stage}`);
7138
+ logger$4.log(`\n🚀 Deploying workspace "${workspace.name}" to Dokploy...`);
7139
+ logger$4.log(` Stage: ${stage}`);
7001
7140
  const imageTag = tag ?? generateTag(stage);
7002
- logger$5.log(` Tag: ${imageTag}`);
7141
+ logger$4.log(` Tag: ${imageTag}`);
7003
7142
  const buildOrder = getAppBuildOrder(workspace);
7004
7143
  let appsToDeployNames = buildOrder;
7005
7144
  if (selectedApps && selectedApps.length > 0) {
7006
7145
  const invalidApps = selectedApps.filter((name) => !workspace.apps[name]);
7007
7146
  if (invalidApps.length > 0) throw new Error(`Unknown apps: ${invalidApps.join(", ")}\nAvailable apps: ${Object.keys(workspace.apps).join(", ")}`);
7008
7147
  appsToDeployNames = buildOrder.filter((name) => selectedApps.includes(name));
7009
- logger$5.log(` Deploying apps: ${appsToDeployNames.join(", ")}`);
7010
- } else logger$5.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
7148
+ logger$4.log(` Deploying apps: ${appsToDeployNames.join(", ")}`);
7149
+ } else logger$4.log(` Deploying all apps: ${appsToDeployNames.join(", ")}`);
7011
7150
  const dokployApps = appsToDeployNames.filter((name) => {
7012
7151
  const target = workspace.apps[name].resolvedDeployTarget;
7013
7152
  if (!isDeployTargetSupported(target)) {
7014
- logger$5.log(` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`);
7153
+ logger$4.log(` ⚠️ Skipping ${name}: ${getDeployTargetError(target, name)}`);
7015
7154
  return false;
7016
7155
  }
7017
7156
  return true;
7018
7157
  });
7019
7158
  if (dokployApps.length === 0) throw new Error("No apps to deploy. All selected apps have unsupported deploy targets.");
7020
7159
  appsToDeployNames = dokployApps;
7021
- logger$5.log("\n🔐 Loading secrets and analyzing environment requirements...");
7160
+ logger$4.log("\n🔐 Loading secrets and analyzing environment requirements...");
7022
7161
  const stageSecrets = await readStageSecrets(stage, workspace.root);
7023
7162
  if (!stageSecrets) {
7024
- logger$5.log(` ⚠️ No secrets found for stage "${stage}"`);
7025
- logger$5.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
7163
+ logger$4.log(` ⚠️ No secrets found for stage "${stage}"`);
7164
+ logger$4.log(` Run "gkm secrets:init --stage ${stage}" to create secrets`);
7026
7165
  }
7027
7166
  const sniffedApps = await sniffAllApps(workspace.apps, workspace.root);
7028
7167
  const encryptedSecrets = stageSecrets ? prepareSecretsForAllApps(stageSecrets, sniffedApps) : /* @__PURE__ */ new Map();
7029
7168
  if (stageSecrets) {
7030
7169
  const report = generateSecretsReport(encryptedSecrets, sniffedApps);
7031
- if (report.appsWithSecrets.length > 0) logger$5.log(` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(", ")}`);
7032
- if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$5.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(", ")}`);
7170
+ if (report.appsWithSecrets.length > 0) logger$4.log(` ✓ Encrypted secrets for: ${report.appsWithSecrets.join(", ")}`);
7171
+ if (report.appsWithMissingSecrets.length > 0) for (const { appName, missing } of report.appsWithMissingSecrets) logger$4.log(` ⚠️ ${appName}: Missing secrets: ${missing.join(", ")}`);
7033
7172
  }
7034
7173
  let creds = await getDokployCredentials();
7035
7174
  if (!creds) {
7036
- logger$5.log("\n📋 Dokploy credentials not found. Let's set them up.");
7175
+ logger$4.log("\n📋 Dokploy credentials not found. Let's set them up.");
7037
7176
  const normalizedEndpoint = (await prompt("Dokploy URL (e.g., https://dokploy.example.com): ")).replace(/\/$/, "");
7038
7177
  try {
7039
7178
  new URL(normalizedEndpoint);
7040
7179
  } catch {
7041
7180
  throw new Error("Invalid URL format");
7042
7181
  }
7043
- logger$5.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
7182
+ logger$4.log(`\nGenerate a token at: ${normalizedEndpoint}/settings/profile\n`);
7044
7183
  const token = await prompt("API Token: ", true);
7045
- logger$5.log("\nValidating credentials...");
7184
+ logger$4.log("\nValidating credentials...");
7046
7185
  if (!await validateDokployToken(normalizedEndpoint, token)) throw new Error("Invalid credentials. Please check your token.");
7047
7186
  await storeDokployCredentials(token, normalizedEndpoint);
7048
7187
  creds = {
7049
7188
  token,
7050
7189
  endpoint: normalizedEndpoint
7051
7190
  };
7052
- logger$5.log("✓ Credentials saved");
7191
+ logger$4.log("✓ Credentials saved");
7053
7192
  }
7054
7193
  const api = new DokployApi({
7055
7194
  baseUrl: creds.endpoint,
7056
7195
  token: creds.token
7057
7196
  });
7058
- logger$5.log("\n📁 Setting up Dokploy project...");
7197
+ logger$4.log("\n📁 Setting up Dokploy project...");
7059
7198
  const projectName = workspace.name;
7060
7199
  let project = (await api.listProjects()).find((p) => p.name.toLowerCase() === projectName.toLowerCase());
7061
7200
  let environmentId;
7062
7201
  if (project) {
7063
- logger$5.log(` Found existing project: ${project.name}`);
7202
+ logger$4.log(` Found existing project: ${project.name}`);
7064
7203
  const matchingEnv = ((await api.getProject(project.projectId)).environments ?? []).find((e) => e.name.toLowerCase() === stage.toLowerCase());
7065
7204
  if (matchingEnv) {
7066
7205
  environmentId = matchingEnv.environmentId;
7067
- logger$5.log(` Using environment: ${matchingEnv.name}`);
7206
+ logger$4.log(` Using environment: ${matchingEnv.name}`);
7068
7207
  } else {
7069
- logger$5.log(` Creating "${stage}" environment...`);
7208
+ logger$4.log(` Creating "${stage}" environment...`);
7070
7209
  environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
7071
- logger$5.log(` ✓ Created environment: ${stage}`);
7210
+ logger$4.log(` ✓ Created environment: ${stage}`);
7072
7211
  }
7073
7212
  } else {
7074
- logger$5.log(` Creating project: ${projectName}`);
7213
+ logger$4.log(` Creating project: ${projectName}`);
7075
7214
  const result = await api.createProject(projectName);
7076
7215
  project = result.project;
7077
7216
  if (result.environment.name.toLowerCase() !== stage.toLowerCase()) {
7078
- logger$5.log(` Creating "${stage}" environment...`);
7217
+ logger$4.log(` Creating "${stage}" environment...`);
7079
7218
  environmentId = (await api.createEnvironment(project.projectId, stage)).environmentId;
7080
7219
  } else environmentId = result.environment.environmentId;
7081
- logger$5.log(` ✓ Created project: ${project.projectId}`);
7220
+ logger$4.log(` ✓ Created project: ${project.projectId}`);
7082
7221
  }
7083
- logger$5.log("\n📋 Loading deploy state...");
7222
+ logger$4.log("\n📋 Loading deploy state...");
7084
7223
  const stateProvider = await createStateProvider({
7085
7224
  config: workspace.state,
7086
7225
  workspaceRoot: workspace.root,
@@ -7088,27 +7227,27 @@ async function workspaceDeployCommand(configured, options) {
7088
7227
  });
7089
7228
  let state = await stateProvider.read(stage);
7090
7229
  if (state) {
7091
- logger$5.log(` Found existing state for stage "${stage}"`);
7230
+ logger$4.log(` Found existing state for stage "${stage}"`);
7092
7231
  if (state.projectId !== project.projectId) {
7093
- logger$5.log(` ⚠ Project ID changed, updating state`);
7232
+ logger$4.log(` ⚠ Project ID changed, updating state`);
7094
7233
  state.projectId = project.projectId;
7095
7234
  }
7096
7235
  if (state.environmentId !== environmentId) {
7097
- logger$5.log(` ⚠ Environment ID changed, updating state`);
7236
+ logger$4.log(` ⚠ Environment ID changed, updating state`);
7098
7237
  state.environmentId = environmentId;
7099
7238
  }
7100
7239
  } else {
7101
- logger$5.log(` Creating new state for stage "${stage}"`);
7240
+ logger$4.log(` Creating new state for stage "${stage}"`);
7102
7241
  state = createEmptyState(stage, project.projectId, environmentId);
7103
7242
  }
7104
- logger$5.log("\n🐳 Checking registry...");
7243
+ logger$4.log("\n🐳 Checking registry...");
7105
7244
  let registryId = await getDokployRegistryId();
7106
7245
  const registry = workspace.deploy.dokploy?.registry;
7107
7246
  if (registryId) try {
7108
7247
  const reg = await api.getRegistry(registryId);
7109
- logger$5.log(` Using registry: ${reg.registryName}`);
7248
+ logger$4.log(` Using registry: ${reg.registryName}`);
7110
7249
  } catch {
7111
- logger$5.log(" ⚠ Stored registry not found, clearing...");
7250
+ logger$4.log(" ⚠ Stored registry not found, clearing...");
7112
7251
  registryId = void 0;
7113
7252
  await storeDokployRegistryId("");
7114
7253
  }
@@ -7117,26 +7256,26 @@ async function workspaceDeployCommand(configured, options) {
7117
7256
  if (registries.length > 0) {
7118
7257
  registryId = registries[0].registryId;
7119
7258
  await storeDokployRegistryId(registryId);
7120
- logger$5.log(` Using registry: ${registries[0].registryName}`);
7259
+ logger$4.log(` Using registry: ${registries[0].registryName}`);
7121
7260
  } else if (registry) {
7122
- logger$5.log(" No registries found in Dokploy. Let's create one.");
7123
- logger$5.log(` Registry URL: ${registry}`);
7261
+ logger$4.log(" No registries found in Dokploy. Let's create one.");
7262
+ logger$4.log(` Registry URL: ${registry}`);
7124
7263
  const username = await prompt("Registry username: ");
7125
7264
  const password = await prompt("Registry password/token: ", true);
7126
7265
  registryId = (await api.createRegistry("Default Registry", registry, username, password)).registryId;
7127
7266
  await storeDokployRegistryId(registryId);
7128
- logger$5.log(` ✓ Registry created: ${registryId}`);
7129
- } else logger$5.log(" ⚠ No registry configured. Set deploy.dokploy.registry in workspace config");
7267
+ logger$4.log(` ✓ Registry created: ${registryId}`);
7268
+ } else logger$4.log(" ⚠ No registry configured. Set deploy.dokploy.registry in workspace config");
7130
7269
  }
7131
7270
  const skippedMobileApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "mobile");
7132
- if (skippedMobileApps.length > 0) logger$5.log(`\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(", ")}`);
7271
+ if (skippedMobileApps.length > 0) logger$4.log(`\n📱 Skipping ${skippedMobileApps.length} mobile app(s) — deploy via framework toolchain: ${skippedMobileApps.join(", ")}`);
7133
7272
  const backendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "backend");
7134
7273
  const frontendApps = appsToDeployNames.filter((name) => workspace.apps[name].type === "web");
7135
7274
  const dokployConfig = workspace.deploy.dokploy;
7136
7275
  let declaredEnv = {};
7137
7276
  let declaredClusters = {};
7138
7277
  {
7139
- logger$5.log("\n📦 Provisioning declared constructs...");
7278
+ logger$4.log("\n📦 Provisioning declared constructs...");
7140
7279
  const appUrls = {};
7141
7280
  for (const appName of backendApps) {
7142
7281
  const app = workspace.apps[appName];
@@ -7154,20 +7293,20 @@ async function workspaceDeployCommand(configured, options) {
7154
7293
  });
7155
7294
  declaredEnv = declared.env;
7156
7295
  declaredClusters = declared.clusters;
7157
- if (Object.keys(declaredEnv).length > 0) logger$5.log(` 🔌 Resolved ${Object.keys(declaredEnv).length} declared URL(s)`);
7296
+ if (Object.keys(declaredEnv).length > 0) logger$4.log(` 🔌 Resolved ${Object.keys(declaredEnv).length} declared URL(s)`);
7158
7297
  if (declared.statements.length > 0) {
7159
7298
  const serverHostname = getServerHostname(creds.endpoint);
7160
7299
  for (const [databaseName, cluster] of Object.entries(declared.clusters)) {
7161
7300
  const statements = declared.statements.filter((statement) => statement.database === databaseName);
7162
7301
  if (statements.length === 0) continue;
7163
7302
  const created = await applyDeclaredStatements(api, cluster, serverHostname, statements);
7164
- logger$5.log(` 🗄️ ${databaseName}: applied ${statements.length} statement(s), ${created} new`);
7303
+ logger$4.log(` 🗄️ ${databaseName}: applied ${statements.length} statement(s), ${created} new`);
7165
7304
  }
7166
7305
  }
7167
7306
  }
7168
7307
  const backupCluster = Object.values(declaredClusters)[0];
7169
7308
  if (workspace.deploy?.backups && backupCluster) {
7170
- logger$5.log("\n💾 Provisioning backup destination...");
7309
+ logger$4.log("\n💾 Provisioning backup destination...");
7171
7310
  const { provisionBackupDestination } = await import("./backup-provisioner-DIVCbfee.mjs");
7172
7311
  const backupState = await provisionBackupDestination({
7173
7312
  api,
@@ -7176,13 +7315,13 @@ async function workspaceDeployCommand(configured, options) {
7176
7315
  stage,
7177
7316
  config: workspace.deploy.backups,
7178
7317
  existingState: getBackupState(state),
7179
- logger: logger$5
7318
+ logger: logger$4
7180
7319
  });
7181
7320
  setBackupState(state, backupState);
7182
7321
  if (!backupState.postgresBackupId) {
7183
7322
  const backupSchedule = workspace.deploy.backups.schedule ?? "0 2 * * *";
7184
7323
  const backupRetention = workspace.deploy.backups.retention ?? 30;
7185
- logger$5.log(" Creating postgres backup schedule...");
7324
+ logger$4.log(" Creating postgres backup schedule...");
7186
7325
  const backup = await api.createPostgresBackup({
7187
7326
  schedule: backupSchedule,
7188
7327
  prefix: `${stage}/postgres`,
@@ -7193,8 +7332,8 @@ async function workspaceDeployCommand(configured, options) {
7193
7332
  keepLatestCount: backupRetention
7194
7333
  });
7195
7334
  setPostgresBackupId(state, backup.backupId);
7196
- logger$5.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
7197
- } else logger$5.log(" ✓ Using existing postgres backup schedule");
7335
+ logger$4.log(` ✓ Postgres backup schedule created (${backupSchedule})`);
7336
+ } else logger$4.log(" ✓ Using existing postgres backup schedule");
7198
7337
  }
7199
7338
  const publicUrls = {};
7200
7339
  const results = [];
@@ -7207,25 +7346,25 @@ async function workspaceDeployCommand(configured, options) {
7207
7346
  frontendUrls.push(`https://${hostname}`);
7208
7347
  }
7209
7348
  if (backendApps.length > 0) {
7210
- logger$5.log("\n📦 PHASE 1: Deploying backend applications...");
7349
+ logger$4.log("\n📦 PHASE 1: Deploying backend applications...");
7211
7350
  for (const appName of backendApps) {
7212
7351
  const app = workspace.apps[appName];
7213
- logger$5.log(`\n ⚙️ Deploying ${appName}...`);
7352
+ logger$4.log(`\n ⚙️ Deploying ${appName}...`);
7214
7353
  try {
7215
7354
  const dokployAppName = applicationName(stage, workspace.name, appName);
7216
7355
  let application = null;
7217
7356
  const cachedAppId = getApplicationId(state, appName);
7218
7357
  if (cachedAppId) {
7219
- logger$5.log(` Using cached ID: ${cachedAppId}`);
7358
+ logger$4.log(` Using cached ID: ${cachedAppId}`);
7220
7359
  application = await api.getApplication(cachedAppId);
7221
- if (application) logger$5.log(` ✓ Application found: ${application.applicationId}`);
7222
- else logger$5.log(` ⚠ Cached ID invalid, will create new`);
7360
+ if (application) logger$4.log(` ✓ Application found: ${application.applicationId}`);
7361
+ else logger$4.log(` ⚠ Cached ID invalid, will create new`);
7223
7362
  }
7224
7363
  if (!application) {
7225
7364
  const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
7226
7365
  application = result.application;
7227
- if (result.created) logger$5.log(` Created application: ${application.applicationId}`);
7228
- else logger$5.log(` Found existing application: ${application.applicationId}`);
7366
+ if (result.created) logger$4.log(` Created application: ${application.applicationId}`);
7367
+ else logger$4.log(` Found existing application: ${application.applicationId}`);
7229
7368
  }
7230
7369
  setApplicationId(state, appName, application.applicationId);
7231
7370
  const appSecrets = encryptedSecrets.get(appName);
@@ -7233,11 +7372,11 @@ async function workspaceDeployCommand(configured, options) {
7233
7372
  if (appSecrets && appSecrets.secretCount > 0) {
7234
7373
  buildArgs.push(`GKM_ENCRYPTED_CREDENTIALS=${appSecrets.payload.encrypted}`);
7235
7374
  buildArgs.push(`GKM_CREDENTIALS_IV=${appSecrets.payload.iv}`);
7236
- logger$5.log(` Encrypted ${appSecrets.secretCount} secrets`);
7375
+ logger$4.log(` Encrypted ${appSecrets.secretCount} secrets`);
7237
7376
  }
7238
7377
  const imageName = `${workspace.name}-${appName}`;
7239
7378
  const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
7240
- logger$5.log(` Building Docker image: ${imageRef}`);
7379
+ logger$4.log(` Building Docker image: ${imageRef}`);
7241
7380
  await deployDocker({
7242
7381
  stage,
7243
7382
  tag: imageTag,
@@ -7278,17 +7417,17 @@ async function workspaceDeployCommand(configured, options) {
7278
7417
  ...declaredEnv
7279
7418
  };
7280
7419
  const envVars = Object.entries(withDeclared).map(([key, value]) => `${key}=${value}`);
7281
- if (Object.keys(withDeclared).length > 0) logger$5.log(` Resolved ${Object.keys(withDeclared).length} env vars: ${Object.keys(withDeclared).sort().join(", ")}`);
7420
+ if (Object.keys(withDeclared).length > 0) logger$4.log(` Resolved ${Object.keys(withDeclared).length} env vars: ${Object.keys(withDeclared).sort().join(", ")}`);
7282
7421
  await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
7283
7422
  await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
7284
- logger$5.log(` Deploying to Dokploy...`);
7423
+ logger$4.log(` Deploying to Dokploy...`);
7285
7424
  await api.deployApplication(application.applicationId);
7286
7425
  const existingDomain = (await api.getDomainsByApplicationId(application.applicationId)).find((d) => d.host === backendHost);
7287
7426
  if (existingDomain) {
7288
7427
  appHostnames.set(appName, backendHost);
7289
7428
  appDomainIds.set(appName, existingDomain.domainId);
7290
7429
  publicUrls[appName] = `https://${backendHost}`;
7291
- logger$5.log(` ✓ Domain: https://${backendHost} (existing)`);
7430
+ logger$4.log(` ✓ Domain: https://${backendHost} (existing)`);
7292
7431
  } else try {
7293
7432
  const domain = await api.createDomain({
7294
7433
  host: backendHost,
@@ -7300,10 +7439,10 @@ async function workspaceDeployCommand(configured, options) {
7300
7439
  appHostnames.set(appName, backendHost);
7301
7440
  appDomainIds.set(appName, domain.domainId);
7302
7441
  publicUrls[appName] = `https://${backendHost}`;
7303
- logger$5.log(` ✓ Domain: https://${backendHost} (created)`);
7442
+ logger$4.log(` ✓ Domain: https://${backendHost} (created)`);
7304
7443
  } catch (domainError) {
7305
7444
  const message = domainError instanceof Error ? domainError.message : "Unknown error";
7306
- logger$5.log(` ⚠ Domain creation failed: ${message}`);
7445
+ logger$4.log(` ⚠ Domain creation failed: ${message}`);
7307
7446
  appHostnames.set(appName, backendHost);
7308
7447
  publicUrls[appName] = `https://${backendHost}`;
7309
7448
  }
@@ -7314,10 +7453,10 @@ async function workspaceDeployCommand(configured, options) {
7314
7453
  applicationId: application.applicationId,
7315
7454
  imageRef
7316
7455
  });
7317
- logger$5.log(` ✓ ${appName} deployed successfully`);
7456
+ logger$4.log(` ✓ ${appName} deployed successfully`);
7318
7457
  } catch (error) {
7319
7458
  const message = error instanceof Error ? error.message : "Unknown error";
7320
- logger$5.log(` ✗ Failed to deploy ${appName}: ${message}`);
7459
+ logger$4.log(` ✗ Failed to deploy ${appName}: ${message}`);
7321
7460
  results.push({
7322
7461
  appName,
7323
7462
  type: app.type,
@@ -7329,25 +7468,25 @@ async function workspaceDeployCommand(configured, options) {
7329
7468
  }
7330
7469
  }
7331
7470
  if (frontendApps.length > 0) {
7332
- logger$5.log("\n🌐 PHASE 2: Deploying frontend applications...");
7471
+ logger$4.log("\n🌐 PHASE 2: Deploying frontend applications...");
7333
7472
  for (const appName of frontendApps) {
7334
7473
  const app = workspace.apps[appName];
7335
- logger$5.log(`\n 🌐 Deploying ${appName}...`);
7474
+ logger$4.log(`\n 🌐 Deploying ${appName}...`);
7336
7475
  try {
7337
7476
  const dokployAppName = applicationName(stage, workspace.name, appName);
7338
7477
  let application = null;
7339
7478
  const cachedAppId = getApplicationId(state, appName);
7340
7479
  if (cachedAppId) {
7341
- logger$5.log(` Using cached ID: ${cachedAppId}`);
7480
+ logger$4.log(` Using cached ID: ${cachedAppId}`);
7342
7481
  application = await api.getApplication(cachedAppId);
7343
- if (application) logger$5.log(` ✓ Application found: ${application.applicationId}`);
7344
- else logger$5.log(` ⚠ Cached ID invalid, will create new`);
7482
+ if (application) logger$4.log(` ✓ Application found: ${application.applicationId}`);
7483
+ else logger$4.log(` ⚠ Cached ID invalid, will create new`);
7345
7484
  }
7346
7485
  if (!application) {
7347
7486
  const result = await api.findOrCreateApplication(dokployAppName, project.projectId, environmentId);
7348
7487
  application = result.application;
7349
- if (result.created) logger$5.log(` Created application: ${application.applicationId}`);
7350
- else logger$5.log(` Found existing application: ${application.applicationId}`);
7488
+ if (result.created) logger$4.log(` Created application: ${application.applicationId}`);
7489
+ else logger$4.log(` Found existing application: ${application.applicationId}`);
7351
7490
  }
7352
7491
  setApplicationId(state, appName, application.applicationId);
7353
7492
  const dependencyUrls = {};
@@ -7367,7 +7506,7 @@ async function workspaceDeployCommand(configured, options) {
7367
7506
  };
7368
7507
  const { valid, missing, resolved } = validateEnvVars(requiredOf(sniffedApps.get(appName)), envContext);
7369
7508
  if (!valid) throw new Error(formatMissingVarsError(appName, missing, stage));
7370
- if (Object.keys(resolved).length > 0) logger$5.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
7509
+ if (Object.keys(resolved).length > 0) logger$4.log(` Resolved ${Object.keys(resolved).length} env vars: ${Object.keys(resolved).join(", ")}`);
7371
7510
  const publicPrefix = getPublicEnvPrefix(app.framework);
7372
7511
  const buildArgs = [];
7373
7512
  const publicUrlArgNames = [];
@@ -7377,10 +7516,10 @@ async function workspaceDeployCommand(configured, options) {
7377
7516
  publicUrlArgNames.push(key);
7378
7517
  }
7379
7518
  }
7380
- if (buildArgs.length > 0) logger$5.log(` Build args: ${publicUrlArgNames.join(", ")}`);
7519
+ if (buildArgs.length > 0) logger$4.log(` Build args: ${publicUrlArgNames.join(", ")}`);
7381
7520
  const imageName = `${workspace.name}-${appName}`;
7382
7521
  const imageRef = registry ? `${registry}/${imageName}:${imageTag}` : `${imageName}:${imageTag}`;
7383
- logger$5.log(` Building Docker image: ${imageRef}`);
7522
+ logger$4.log(` Building Docker image: ${imageRef}`);
7384
7523
  await deployDocker({
7385
7524
  stage,
7386
7525
  tag: imageTag,
@@ -7401,14 +7540,14 @@ async function workspaceDeployCommand(configured, options) {
7401
7540
  for (const [key, value] of Object.entries(resolved)) envVars.push(`${key}=${value}`);
7402
7541
  await api.saveDockerProvider(application.applicationId, imageRef, { registryId });
7403
7542
  await api.saveApplicationEnv(application.applicationId, envVars.join("\n"));
7404
- logger$5.log(` Deploying to Dokploy...`);
7543
+ logger$4.log(` Deploying to Dokploy...`);
7405
7544
  await api.deployApplication(application.applicationId);
7406
7545
  const existingFrontendDomain = (await api.getDomainsByApplicationId(application.applicationId)).find((d) => d.host === frontendHost);
7407
7546
  if (existingFrontendDomain) {
7408
7547
  appHostnames.set(appName, frontendHost);
7409
7548
  appDomainIds.set(appName, existingFrontendDomain.domainId);
7410
7549
  publicUrls[appName] = `https://${frontendHost}`;
7411
- logger$5.log(` ✓ Domain: https://${frontendHost} (existing)`);
7550
+ logger$4.log(` ✓ Domain: https://${frontendHost} (existing)`);
7412
7551
  } else try {
7413
7552
  const domain = await api.createDomain({
7414
7553
  host: frontendHost,
@@ -7420,10 +7559,10 @@ async function workspaceDeployCommand(configured, options) {
7420
7559
  appHostnames.set(appName, frontendHost);
7421
7560
  appDomainIds.set(appName, domain.domainId);
7422
7561
  publicUrls[appName] = `https://${frontendHost}`;
7423
- logger$5.log(` ✓ Domain: https://${frontendHost} (created)`);
7562
+ logger$4.log(` ✓ Domain: https://${frontendHost} (created)`);
7424
7563
  } catch (domainError) {
7425
7564
  const message = domainError instanceof Error ? domainError.message : "Unknown error";
7426
- logger$5.log(` ⚠ Domain creation failed: ${message}`);
7565
+ logger$4.log(` ⚠ Domain creation failed: ${message}`);
7427
7566
  appHostnames.set(appName, frontendHost);
7428
7567
  publicUrls[appName] = `https://${frontendHost}`;
7429
7568
  }
@@ -7434,10 +7573,10 @@ async function workspaceDeployCommand(configured, options) {
7434
7573
  applicationId: application.applicationId,
7435
7574
  imageRef
7436
7575
  });
7437
- logger$5.log(` ✓ ${appName} deployed successfully`);
7576
+ logger$4.log(` ✓ ${appName} deployed successfully`);
7438
7577
  } catch (error) {
7439
7578
  const message = error instanceof Error ? error.message : "Unknown error";
7440
- logger$5.log(` ✗ Failed to deploy ${appName}: ${message}`);
7579
+ logger$4.log(` ✗ Failed to deploy ${appName}: ${message}`);
7441
7580
  results.push({
7442
7581
  appName,
7443
7582
  type: app.type,
@@ -7447,9 +7586,9 @@ async function workspaceDeployCommand(configured, options) {
7447
7586
  }
7448
7587
  }
7449
7588
  }
7450
- logger$5.log("\n📋 Saving deploy state...");
7589
+ logger$4.log("\n📋 Saving deploy state...");
7451
7590
  await stateProvider.write(stage, state);
7452
- logger$5.log(" ✓ State saved");
7591
+ logger$4.log(" ✓ State saved");
7453
7592
  const dnsConfig = workspace.deploy.dns;
7454
7593
  if (dnsConfig && appHostnames.size > 0) {
7455
7594
  const dnsResult = await orchestrateDns(appHostnames, dnsConfig, creds.endpoint);
@@ -7458,27 +7597,27 @@ async function workspaceDeployCommand(configured, options) {
7458
7597
  await stateProvider.write(stage, state);
7459
7598
  }
7460
7599
  if (dnsResult?.success && appHostnames.size > 0) {
7461
- logger$5.log("\n🔒 Validating domains for SSL certificates...");
7600
+ logger$4.log("\n🔒 Validating domains for SSL certificates...");
7462
7601
  for (const [appName, hostname] of appHostnames) try {
7463
7602
  const result = await api.validateDomain(hostname);
7464
- if (result.isValid) logger$5.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);
7465
- else logger$5.log(` ⚠ ${appName}: ${hostname} not valid`);
7603
+ if (result.isValid) logger$4.log(` ✓ ${appName}: ${hostname} → ${result.resolvedIp}`);
7604
+ else logger$4.log(` ⚠ ${appName}: ${hostname} not valid`);
7466
7605
  } catch (validationError) {
7467
7606
  const message = validationError instanceof Error ? validationError.message : "Unknown error";
7468
- logger$5.log(` ⚠ ${appName}: validation failed - ${message}`);
7607
+ logger$4.log(` ⚠ ${appName}: validation failed - ${message}`);
7469
7608
  }
7470
7609
  }
7471
7610
  }
7472
7611
  const successCount = results.filter((r) => r.success).length;
7473
7612
  const failedCount = results.filter((r) => !r.success).length;
7474
- logger$5.log(`\n${"─".repeat(50)}`);
7475
- logger$5.log(`\n✅ Workspace deployment complete!`);
7476
- logger$5.log(` Project: ${project.projectId}`);
7477
- logger$5.log(` Successful: ${successCount}`);
7478
- if (failedCount > 0) logger$5.log(` Failed: ${failedCount}`);
7613
+ logger$4.log(`\n${"─".repeat(50)}`);
7614
+ logger$4.log(`\n✅ Workspace deployment complete!`);
7615
+ logger$4.log(` Project: ${project.projectId}`);
7616
+ logger$4.log(` Successful: ${successCount}`);
7617
+ if (failedCount > 0) logger$4.log(` Failed: ${failedCount}`);
7479
7618
  if (Object.keys(publicUrls).length > 0) {
7480
- logger$5.log("\n 📡 Deployed URLs:");
7481
- for (const [name, url] of Object.entries(publicUrls)) logger$5.log(` ${name}: ${url}`);
7619
+ logger$4.log("\n 📡 Deployed URLs:");
7620
+ for (const [name, url] of Object.entries(publicUrls)) logger$4.log(` ${name}: ${url}`);
7482
7621
  }
7483
7622
  return {
7484
7623
  apps: results,
@@ -7497,7 +7636,7 @@ async function deployCommand(options) {
7497
7636
  }
7498
7637
  //#endregion
7499
7638
  //#region src/deploy/init.ts
7500
- const logger$4 = console;
7639
+ const logger$3 = console;
7501
7640
  /**
7502
7641
  * Get the Dokploy API token from stored credentials or environment
7503
7642
  */
@@ -7543,28 +7682,28 @@ function unsafeToRewrite(content) {
7543
7682
  async function updateConfig(config, cwd = process.cwd()) {
7544
7683
  const configPath = join(cwd, "gkm.config.ts");
7545
7684
  if (!existsSync(configPath)) {
7546
- logger$4.warn("\n gkm.config.ts not found. Add this configuration manually:\n");
7547
- logger$4.log(` providers: {`);
7548
- logger$4.log(` dokploy: {`);
7549
- logger$4.log(` endpoint: '${config.endpoint}',`);
7550
- logger$4.log(` projectId: '${config.projectId}',`);
7551
- logger$4.log(` applicationId: '${config.applicationId}',`);
7552
- logger$4.log(` },`);
7553
- logger$4.log(` },`);
7685
+ logger$3.warn("\n gkm.config.ts not found. Add this configuration manually:\n");
7686
+ logger$3.log(` providers: {`);
7687
+ logger$3.log(` dokploy: {`);
7688
+ logger$3.log(` endpoint: '${config.endpoint}',`);
7689
+ logger$3.log(` projectId: '${config.projectId}',`);
7690
+ logger$3.log(` applicationId: '${config.applicationId}',`);
7691
+ logger$3.log(` },`);
7692
+ logger$3.log(` },`);
7554
7693
  return;
7555
7694
  }
7556
7695
  const content = await readFile(configPath, "utf-8");
7557
7696
  if (unsafeToRewrite(content)) {
7558
- logger$4.warn("\n gkm.config.ts holds a dokploy block this cannot safely rewrite —\n it contains an expression or a nested object. Leaving it alone.");
7559
- logger$4.log(`\n Discovered, if you want them written down:`);
7560
- logger$4.log(` projectId: '${config.projectId}'`);
7561
- logger$4.log(` applicationId: '${config.applicationId}'`);
7562
- if (config.registryId) logger$4.log(` registryId: '${config.registryId}'`);
7697
+ logger$3.warn("\n gkm.config.ts holds a dokploy block this cannot safely rewrite —\n it contains an expression or a nested object. Leaving it alone.");
7698
+ logger$3.log(`\n Discovered, if you want them written down:`);
7699
+ logger$3.log(` projectId: '${config.projectId}'`);
7700
+ logger$3.log(` applicationId: '${config.applicationId}'`);
7701
+ if (config.registryId) logger$3.log(` registryId: '${config.registryId}'`);
7563
7702
  return;
7564
7703
  }
7565
7704
  if (content.includes("dokploy:") && content.includes("applicationId:")) {
7566
- logger$4.log("\n Dokploy config already exists in gkm.config.ts");
7567
- logger$4.log(" Updating with new values...");
7705
+ logger$3.log("\n Dokploy config already exists in gkm.config.ts");
7706
+ logger$3.log(" Updating with new values...");
7568
7707
  }
7569
7708
  const registryLine = config.registryId ? `\n\t\t\tregistryId: '${config.registryId}',` : "";
7570
7709
  const dokployConfigStr = `dokploy: {
@@ -7582,7 +7721,7 @@ async function updateConfig(config, cwd = process.cwd()) {
7582
7721
  },
7583
7722
  });`);
7584
7723
  await writeFile(configPath, newContent);
7585
- logger$4.log("\n ✓ Updated gkm.config.ts with Dokploy configuration");
7724
+ logger$3.log("\n ✓ Updated gkm.config.ts with Dokploy configuration");
7586
7725
  }
7587
7726
  /**
7588
7727
  * Initialize Dokploy deployment configuration
@@ -7591,22 +7730,22 @@ async function deployInitCommand(options) {
7591
7730
  const { projectName, appName, projectId: existingProjectId, registryId } = options;
7592
7731
  const endpoint = await getEndpoint(options.endpoint);
7593
7732
  const api = await createApi(endpoint);
7594
- logger$4.log(`\n🚀 Initializing Dokploy deployment...`);
7595
- logger$4.log(` Endpoint: ${endpoint}`);
7733
+ logger$3.log(`\n🚀 Initializing Dokploy deployment...`);
7734
+ logger$3.log(` Endpoint: ${endpoint}`);
7596
7735
  let projectId;
7597
7736
  if (existingProjectId) {
7598
7737
  projectId = existingProjectId;
7599
- logger$4.log(`\n📁 Using existing project: ${projectId}`);
7738
+ logger$3.log(`\n📁 Using existing project: ${projectId}`);
7600
7739
  } else {
7601
- logger$4.log(`\n📁 Looking for project: ${projectName}`);
7740
+ logger$3.log(`\n📁 Looking for project: ${projectName}`);
7602
7741
  const existingProject = (await api.listProjects()).find((p) => p.name.toLowerCase() === projectName.toLowerCase());
7603
7742
  if (existingProject) {
7604
7743
  projectId = existingProject.projectId;
7605
- logger$4.log(` Found existing project: ${projectId}`);
7744
+ logger$3.log(` Found existing project: ${projectId}`);
7606
7745
  } else {
7607
- logger$4.log(` Creating new project...`);
7746
+ logger$3.log(` Creating new project...`);
7608
7747
  projectId = (await api.createProject(projectName)).project.projectId;
7609
- logger$4.log(` ✓ Created project: ${projectId}`);
7748
+ logger$3.log(` ✓ Created project: ${projectId}`);
7610
7749
  }
7611
7750
  }
7612
7751
  const project = await api.getProject(projectId);
@@ -7614,22 +7753,22 @@ async function deployInitCommand(options) {
7614
7753
  const firstEnv = project.environments?.[0];
7615
7754
  if (firstEnv) environmentId = firstEnv.environmentId;
7616
7755
  else {
7617
- logger$4.log(` Creating production environment...`);
7756
+ logger$3.log(` Creating production environment...`);
7618
7757
  environmentId = (await api.createEnvironment(projectId, "production")).environmentId;
7619
7758
  }
7620
- logger$4.log(`\n📦 Creating application: ${appName}`);
7759
+ logger$3.log(`\n📦 Creating application: ${appName}`);
7621
7760
  const application = await api.createApplication(appName, projectId, environmentId);
7622
- logger$4.log(` ✓ Created application: ${application.applicationId}`);
7761
+ logger$3.log(` ✓ Created application: ${application.applicationId}`);
7623
7762
  if (registryId) {
7624
- logger$4.log(`\n🔧 Configuring registry: ${registryId}`);
7763
+ logger$3.log(`\n🔧 Configuring registry: ${registryId}`);
7625
7764
  await api.updateApplication(application.applicationId, { registryId });
7626
- logger$4.log(` ✓ Registry configured`);
7765
+ logger$3.log(` ✓ Registry configured`);
7627
7766
  } else try {
7628
7767
  const registries = await api.listRegistries();
7629
7768
  if (registries.length > 0) {
7630
- logger$4.log(`\n📋 Available registries:`);
7631
- for (const reg of registries) logger$4.log(` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`);
7632
- logger$4.log(`\n To use a registry, run with --registry-id <id>`);
7769
+ logger$3.log(`\n📋 Available registries:`);
7770
+ for (const reg of registries) logger$3.log(` - ${reg.registryName}: ${reg.registryUrl} (${reg.registryId})`);
7771
+ logger$3.log(`\n To use a registry, run with --registry-id <id>`);
7633
7772
  }
7634
7773
  } catch {}
7635
7774
  const config = {
@@ -7638,14 +7777,14 @@ async function deployInitCommand(options) {
7638
7777
  applicationId: application.applicationId
7639
7778
  };
7640
7779
  await updateConfig(config);
7641
- logger$4.log(`\n✅ Dokploy deployment initialized!`);
7642
- logger$4.log(`\n📋 Configuration:`);
7643
- logger$4.log(` Project ID: ${projectId}`);
7644
- logger$4.log(` Application ID: ${application.applicationId}`);
7645
- logger$4.log(`\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);
7646
- logger$4.log(`\n📝 Next steps:`);
7647
- logger$4.log(` 1. Initialize secrets: gkm secrets:init --stage production`);
7648
- logger$4.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);
7780
+ logger$3.log(`\n✅ Dokploy deployment initialized!`);
7781
+ logger$3.log(`\n📋 Configuration:`);
7782
+ logger$3.log(` Project ID: ${projectId}`);
7783
+ logger$3.log(` Application ID: ${application.applicationId}`);
7784
+ logger$3.log(`\n🔗 View in Dokploy: ${endpoint}/project/${projectId}`);
7785
+ logger$3.log(`\n📝 Next steps:`);
7786
+ logger$3.log(` 1. Initialize secrets: gkm secrets:init --stage production`);
7787
+ logger$3.log(` 2. Deploy: gkm deploy --provider dokploy --stage production`);
7649
7788
  return config;
7650
7789
  }
7651
7790
  /**
@@ -7656,31 +7795,31 @@ async function deployListCommand(options) {
7656
7795
  const api = await createApi(endpoint);
7657
7796
  const { resource } = options;
7658
7797
  if (resource === "projects") {
7659
- logger$4.log(`\n📁 Projects in ${endpoint}:`);
7798
+ logger$3.log(`\n📁 Projects in ${endpoint}:`);
7660
7799
  const projects = await api.listProjects();
7661
7800
  if (projects.length === 0) {
7662
- logger$4.log(" No projects found");
7801
+ logger$3.log(" No projects found");
7663
7802
  return;
7664
7803
  }
7665
7804
  for (const project of projects) {
7666
- logger$4.log(`\n ${project.name} (${project.projectId})`);
7667
- if (project.description) logger$4.log(` ${project.description}`);
7805
+ logger$3.log(`\n ${project.name} (${project.projectId})`);
7806
+ if (project.description) logger$3.log(` ${project.description}`);
7668
7807
  }
7669
7808
  } else if (resource === "registries") {
7670
- logger$4.log(`\n🐳 Registries in ${endpoint}:`);
7809
+ logger$3.log(`\n🐳 Registries in ${endpoint}:`);
7671
7810
  const registries = await api.listRegistries();
7672
7811
  if (registries.length === 0) {
7673
- logger$4.log(" No registries configured");
7674
- logger$4.log(" Run \"gkm registry:setup\" to configure a registry");
7812
+ logger$3.log(" No registries configured");
7813
+ logger$3.log(" Run \"gkm registry:setup\" to configure a registry");
7675
7814
  return;
7676
7815
  }
7677
7816
  const storedRegistryId = await getDokployRegistryId();
7678
7817
  for (const registry of registries) {
7679
7818
  const marker = registry.registryId === storedRegistryId ? " (default)" : "";
7680
- logger$4.log(`\n ${registry.registryName}${marker} (${registry.registryId})`);
7681
- logger$4.log(` URL: ${registry.registryUrl}`);
7682
- logger$4.log(` Username: ${registry.username}`);
7683
- if (registry.imagePrefix) logger$4.log(` Prefix: ${registry.imagePrefix}`);
7819
+ logger$3.log(`\n ${registry.registryName}${marker} (${registry.registryId})`);
7820
+ logger$3.log(` URL: ${registry.registryUrl}`);
7821
+ logger$3.log(` Username: ${registry.username}`);
7822
+ if (registry.imagePrefix) logger$3.log(` Prefix: ${registry.imagePrefix}`);
7684
7823
  }
7685
7824
  }
7686
7825
  }
@@ -8541,26 +8680,26 @@ function loadPackageJson() {
8541
8680
  * Run: pnpm --filter @geekmidas/cli sync-versions
8542
8681
  */
8543
8682
  const GEEKMIDAS_VERSIONS = {
8544
- "@geekmidas/audit": "~10.0.0-alpha.27",
8545
- "@geekmidas/auth": "~10.0.0-alpha.27",
8546
- "@geekmidas/cache": "~10.0.0-alpha.27",
8547
- "@geekmidas/client": "~10.0.0-alpha.27",
8548
- "@geekmidas/cloud": "~10.0.0-alpha.27",
8549
- "@geekmidas/constructs": "~10.0.0-alpha.27",
8550
- "@geekmidas/db": "~10.0.0-alpha.27",
8551
- "@geekmidas/emailkit": "~10.0.0-alpha.27",
8552
- "@geekmidas/envkit": "~10.0.0-alpha.27",
8553
- "@geekmidas/errors": "~10.0.0-alpha.27",
8554
- "@geekmidas/events": "~10.0.0-alpha.27",
8555
- "@geekmidas/logger": "~10.0.0-alpha.27",
8556
- "@geekmidas/manifest": "~10.0.0-alpha.27",
8557
- "@geekmidas/rate-limit": "~10.0.0-alpha.27",
8558
- "@geekmidas/schema": "~10.0.0-alpha.27",
8559
- "@geekmidas/services": "~10.0.0-alpha.27",
8560
- "@geekmidas/storage": "~10.0.0-alpha.27",
8561
- "@geekmidas/studio": "~10.0.0-alpha.27",
8562
- "@geekmidas/telescope": "~10.0.0-alpha.27",
8563
- "@geekmidas/testkit": "~10.0.0-alpha.27",
8683
+ "@geekmidas/audit": "~10.0.0-alpha.28",
8684
+ "@geekmidas/auth": "~10.0.0-alpha.28",
8685
+ "@geekmidas/cache": "~10.0.0-alpha.28",
8686
+ "@geekmidas/client": "~10.0.0-alpha.28",
8687
+ "@geekmidas/cloud": "~10.0.0-alpha.28",
8688
+ "@geekmidas/constructs": "~10.0.0-alpha.28",
8689
+ "@geekmidas/db": "~10.0.0-alpha.28",
8690
+ "@geekmidas/emailkit": "~10.0.0-alpha.28",
8691
+ "@geekmidas/envkit": "~10.0.0-alpha.28",
8692
+ "@geekmidas/errors": "~10.0.0-alpha.28",
8693
+ "@geekmidas/events": "~10.0.0-alpha.28",
8694
+ "@geekmidas/logger": "~10.0.0-alpha.28",
8695
+ "@geekmidas/manifest": "~10.0.0-alpha.28",
8696
+ "@geekmidas/rate-limit": "~10.0.0-alpha.28",
8697
+ "@geekmidas/schema": "~10.0.0-alpha.28",
8698
+ "@geekmidas/services": "~10.0.0-alpha.28",
8699
+ "@geekmidas/storage": "~10.0.0-alpha.28",
8700
+ "@geekmidas/studio": "~10.0.0-alpha.28",
8701
+ "@geekmidas/telescope": "~10.0.0-alpha.28",
8702
+ "@geekmidas/testkit": "~10.0.0-alpha.28",
8564
8703
  "@geekmidas/cli": `~${loadPackageJson().version}`
8565
8704
  };
8566
8705
  //#endregion
@@ -14639,7 +14778,7 @@ var NotAnAwsRegion = class extends Error {
14639
14778
  };
14640
14779
  //#endregion
14641
14780
  //#region src/secrets/index.ts
14642
- const logger$3 = console;
14781
+ const logger$2 = console;
14643
14782
  /**
14644
14783
  * Initialize secrets for a stage.
14645
14784
  * Generates secure random passwords for configured services.
@@ -14647,7 +14786,7 @@ const logger$3 = console;
14647
14786
  async function secretsInitCommand(options) {
14648
14787
  const { stage, force } = options;
14649
14788
  if (!force && secretsExist(stage)) {
14650
- logger$3.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
14789
+ logger$2.error(`Secrets already exist for stage "${stage}". Use --force to overwrite.`);
14651
14790
  process.exit(1);
14652
14791
  }
14653
14792
  let projectName;
@@ -14657,21 +14796,21 @@ async function secretsInitCommand(options) {
14657
14796
  projectName = loaded.workspace.name;
14658
14797
  if (Object.keys(loaded.workspace.apps).length > 1) {
14659
14798
  workspaceSecrets = generateFullstackCustomSecrets(loaded.workspace);
14660
- logger$3.log(" Detected workspace mode — generating per-app secrets");
14799
+ logger$2.log(" Detected workspace mode — generating per-app secrets");
14661
14800
  }
14662
14801
  } catch {}
14663
14802
  const secrets = createStageSecrets(stage, [], { projectName });
14664
14803
  if (workspaceSecrets) secrets.custom = workspaceSecrets;
14665
14804
  await writeStageSecrets(secrets);
14666
- logger$3.log(`\n✓ Secrets initialized for stage "${stage}"`);
14667
- logger$3.log(` Location: .gkm/secrets/${stage}.json`);
14668
- if (secrets.urls.DATABASE_URL) logger$3.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
14669
- if (secrets.urls.REDIS_URL) logger$3.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
14670
- if (secrets.urls.RABBITMQ_URL) logger$3.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
14671
- if (secrets.urls.STORAGE_ENDPOINT) logger$3.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
14672
- if (Object.keys(secrets.custom).length > 0) logger$3.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
14673
- logger$3.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
14674
- logger$3.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
14805
+ logger$2.log(`\n✓ Secrets initialized for stage "${stage}"`);
14806
+ logger$2.log(` Location: .gkm/secrets/${stage}.json`);
14807
+ if (secrets.urls.DATABASE_URL) logger$2.log(`\n DATABASE_URL: ${maskUrl(secrets.urls.DATABASE_URL)}`);
14808
+ if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${maskUrl(secrets.urls.REDIS_URL)}`);
14809
+ if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${maskUrl(secrets.urls.RABBITMQ_URL)}`);
14810
+ if (secrets.urls.STORAGE_ENDPOINT) logger$2.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
14811
+ if (Object.keys(secrets.custom).length > 0) logger$2.log(`\n Custom secrets: ${Object.keys(secrets.custom).length}`);
14812
+ logger$2.log(`\n Use "gkm secrets:show --stage ${stage}" to view secrets`);
14813
+ logger$2.log(" Use \"gkm secrets:set <KEY> <VALUE> --stage " + stage + "\" to add custom secrets");
14675
14814
  }
14676
14815
  /**
14677
14816
  * Read all data from stdin.
@@ -14690,21 +14829,21 @@ async function secretsSetCommand(key, value, options) {
14690
14829
  let secretValue = value;
14691
14830
  if (!secretValue) {
14692
14831
  if (process.stdin.isTTY) {
14693
- logger$3.error("No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>");
14694
- logger$3.error("Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>");
14832
+ logger$2.error("No value provided. Use: gkm secrets:set KEY VALUE --stage <stage>");
14833
+ logger$2.error("Or pipe from stdin: echo \"value\" | gkm secrets:set KEY --stage <stage>");
14695
14834
  process.exit(1);
14696
14835
  }
14697
14836
  secretValue = await readStdin();
14698
14837
  if (!secretValue) {
14699
- logger$3.error("No value received from stdin");
14838
+ logger$2.error("No value received from stdin");
14700
14839
  process.exit(1);
14701
14840
  }
14702
14841
  }
14703
14842
  try {
14704
14843
  await setCustomSecret(stage, key, secretValue);
14705
- logger$3.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
14844
+ logger$2.log(`\n✓ Secret "${key}" set for stage "${stage}"`);
14706
14845
  } catch (error) {
14707
- logger$3.error(error instanceof Error ? error.message : "Failed to set secret");
14846
+ logger$2.error(error instanceof Error ? error.message : "Failed to set secret");
14708
14847
  process.exit(1);
14709
14848
  }
14710
14849
  }
@@ -14715,33 +14854,33 @@ async function secretsShowCommand(options) {
14715
14854
  const { stage, reveal } = options;
14716
14855
  const secrets = await readStageSecrets(stage);
14717
14856
  if (!secrets) {
14718
- logger$3.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14857
+ logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14719
14858
  process.exit(1);
14720
14859
  }
14721
- logger$3.log(`\nSecrets for stage "${stage}":`);
14722
- logger$3.log(` Created: ${secrets.createdAt}`);
14723
- logger$3.log(` Updated: ${secrets.updatedAt}`);
14724
- logger$3.log("\nService Credentials:");
14860
+ logger$2.log(`\nSecrets for stage "${stage}":`);
14861
+ logger$2.log(` Created: ${secrets.createdAt}`);
14862
+ logger$2.log(` Updated: ${secrets.updatedAt}`);
14863
+ logger$2.log("\nService Credentials:");
14725
14864
  for (const [service, creds] of Object.entries(secrets.services)) if (creds) {
14726
- logger$3.log(`\n ${service}:`);
14727
- logger$3.log(` host: ${creds.host}`);
14728
- logger$3.log(` port: ${creds.port}`);
14729
- logger$3.log(` username: ${creds.username}`);
14730
- logger$3.log(` password: ${reveal ? creds.password : maskPassword(creds.password)}`);
14731
- if (creds.database) logger$3.log(` database: ${creds.database}`);
14732
- if (creds.vhost) logger$3.log(` vhost: ${creds.vhost}`);
14733
- if (creds.bucket) logger$3.log(` bucket: ${creds.bucket}`);
14734
- }
14735
- logger$3.log("\nConnection URLs:");
14736
- if (secrets.urls.DATABASE_URL) logger$3.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
14737
- if (secrets.urls.REDIS_URL) logger$3.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
14738
- if (secrets.urls.RABBITMQ_URL) logger$3.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
14739
- if (secrets.urls.STORAGE_ENDPOINT) logger$3.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
14865
+ logger$2.log(`\n ${service}:`);
14866
+ logger$2.log(` host: ${creds.host}`);
14867
+ logger$2.log(` port: ${creds.port}`);
14868
+ logger$2.log(` username: ${creds.username}`);
14869
+ logger$2.log(` password: ${reveal ? creds.password : maskPassword(creds.password)}`);
14870
+ if (creds.database) logger$2.log(` database: ${creds.database}`);
14871
+ if (creds.vhost) logger$2.log(` vhost: ${creds.vhost}`);
14872
+ if (creds.bucket) logger$2.log(` bucket: ${creds.bucket}`);
14873
+ }
14874
+ logger$2.log("\nConnection URLs:");
14875
+ if (secrets.urls.DATABASE_URL) logger$2.log(` DATABASE_URL: ${reveal ? secrets.urls.DATABASE_URL : maskUrl(secrets.urls.DATABASE_URL)}`);
14876
+ if (secrets.urls.REDIS_URL) logger$2.log(` REDIS_URL: ${reveal ? secrets.urls.REDIS_URL : maskUrl(secrets.urls.REDIS_URL)}`);
14877
+ if (secrets.urls.RABBITMQ_URL) logger$2.log(` RABBITMQ_URL: ${reveal ? secrets.urls.RABBITMQ_URL : maskUrl(secrets.urls.RABBITMQ_URL)}`);
14878
+ if (secrets.urls.STORAGE_ENDPOINT) logger$2.log(` STORAGE_ENDPOINT: ${secrets.urls.STORAGE_ENDPOINT}`);
14740
14879
  if (Object.keys(secrets.custom).length > 0) {
14741
- logger$3.log("\nCustom Secrets:");
14742
- for (const [key, value] of Object.entries(secrets.custom)) logger$3.log(` ${key}: ${reveal ? value : maskPassword(value)}`);
14880
+ logger$2.log("\nCustom Secrets:");
14881
+ for (const [key, value] of Object.entries(secrets.custom)) logger$2.log(` ${key}: ${reveal ? value : maskPassword(value)}`);
14743
14882
  }
14744
- if (!reveal) logger$3.log("\nUse --reveal to show actual values");
14883
+ if (!reveal) logger$2.log("\nUse --reveal to show actual values");
14745
14884
  }
14746
14885
  /**
14747
14886
  * Rotate passwords for services.
@@ -14750,25 +14889,25 @@ async function secretsRotateCommand(options) {
14750
14889
  const { stage, service } = options;
14751
14890
  const secrets = await readStageSecrets(stage);
14752
14891
  if (!secrets) {
14753
- logger$3.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14892
+ logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14754
14893
  process.exit(1);
14755
14894
  }
14756
14895
  if (service) {
14757
14896
  if (!secrets.services[service]) {
14758
- logger$3.error(`Service "${service}" not configured in stage "${stage}"`);
14897
+ logger$2.error(`Service "${service}" not configured in stage "${stage}"`);
14759
14898
  process.exit(1);
14760
14899
  }
14761
14900
  const updated = rotateServicePassword(secrets, service);
14762
14901
  await writeStageSecrets(updated);
14763
- logger$3.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
14902
+ logger$2.log(`\n✓ Password rotated for ${service} in stage "${stage}"`);
14764
14903
  } else {
14765
14904
  let updated = secrets;
14766
14905
  const services = Object.keys(secrets.services);
14767
14906
  for (const svc of services) updated = rotateServicePassword(updated, svc);
14768
14907
  await writeStageSecrets(updated);
14769
- logger$3.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
14908
+ logger$2.log(`\n✓ Passwords rotated for all services in stage "${stage}": ${services.join(", ")}`);
14770
14909
  }
14771
- logger$3.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
14910
+ logger$2.log(`\nUse "gkm secrets:show --stage ${stage}" to view new values`);
14772
14911
  }
14773
14912
  /**
14774
14913
  * Import secrets from a JSON file.
@@ -14776,7 +14915,7 @@ async function secretsRotateCommand(options) {
14776
14915
  async function secretsImportCommand(file, options) {
14777
14916
  const { stage, merge = true } = options;
14778
14917
  if (!existsSync(file)) {
14779
- logger$3.error(`File not found: ${file}`);
14918
+ logger$2.error(`File not found: ${file}`);
14780
14919
  process.exit(1);
14781
14920
  }
14782
14921
  let importedSecrets;
@@ -14786,12 +14925,12 @@ async function secretsImportCommand(file, options) {
14786
14925
  if (typeof importedSecrets !== "object" || importedSecrets === null) throw new Error("JSON must be an object");
14787
14926
  for (const [key, value] of Object.entries(importedSecrets)) if (typeof value !== "string") throw new Error(`Value for "${key}" must be a string, got ${typeof value}`);
14788
14927
  } catch (error) {
14789
- logger$3.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
14928
+ logger$2.error(`Failed to parse JSON file: ${error instanceof Error ? error.message : "Invalid JSON"}`);
14790
14929
  process.exit(1);
14791
14930
  }
14792
14931
  const secrets = await readStageSecrets(stage);
14793
14932
  if (!secrets) {
14794
- logger$3.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14933
+ logger$2.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
14795
14934
  process.exit(1);
14796
14935
  }
14797
14936
  const updatedCustom = merge ? {
@@ -14806,10 +14945,10 @@ async function secretsImportCommand(file, options) {
14806
14945
  await writeStageSecrets(updated);
14807
14946
  const importedCount = Object.keys(importedSecrets).length;
14808
14947
  const totalCount = Object.keys(updatedCustom).length;
14809
- logger$3.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
14810
- if (merge && totalCount > importedCount) logger$3.log(` Total custom secrets: ${totalCount}`);
14811
- logger$3.log("\n Imported keys:");
14812
- for (const key of Object.keys(importedSecrets)) logger$3.log(` - ${key}`);
14948
+ logger$2.log(`\n✓ Imported ${importedCount} secrets for stage "${stage}"`);
14949
+ if (merge && totalCount > importedCount) logger$2.log(` Total custom secrets: ${totalCount}`);
14950
+ logger$2.log("\n Imported keys:");
14951
+ for (const key of Object.keys(importedSecrets)) logger$2.log(` - ${key}`);
14813
14952
  }
14814
14953
  /**
14815
14954
  * Mask password in a URL for display.
@@ -14824,175 +14963,120 @@ function maskUrl(url) {
14824
14963
  }
14825
14964
  }
14826
14965
  //#endregion
14827
- //#region src/trust/index.ts
14828
- /**
14829
- * Installing the local edge's certificate authority into the OS trust store.
14830
- *
14831
- * `NODE_EXTRA_CA_CERTS` covers everything gkm starts — the dev server, the test
14832
- * suite, anything under `gkm exec` — because reconcile injects it. A browser
14833
- * reads none of that: it asks the operating system, so until the root is in the
14834
- * system store a local `https://` address is "not secure", which is true and
14835
- * unhelpful.
14836
- *
14837
- * A separate command rather than part of `gkm setup`, for one reason: this
14838
- * needs `sudo`, and a setup that silently escalates on every run is a worse
14839
- * trade than one that tells you what to type. Caddy's own `caddy trust` draws
14840
- * the line in the same place.
14841
- */
14842
- const logger$2 = console;
14843
- const run = promisify(execFile);
14844
- /**
14845
- * How each platform is told about a new authority.
14846
- *
14847
- * Separate rather than shelled through one script because the failure modes
14848
- * differ and the message should name the real one: macOS refuses without an
14849
- * admin password, Debian needs the file in a specific directory before its
14850
- * updater will look at it.
14851
- */
14852
- function installation(platform, certificate) {
14853
- if (platform === "darwin") return {
14854
- describe: "the System keychain",
14855
- steps: [["sudo", [
14856
- "security",
14857
- "add-trusted-cert",
14858
- "-d",
14859
- "-r",
14860
- "trustRoot",
14861
- "-k",
14862
- "/Library/Keychains/System.keychain",
14863
- certificate
14864
- ]]]
14865
- };
14866
- if (platform === "linux") {
14867
- const debian = existsSync("/usr/local/share/ca-certificates");
14868
- return {
14869
- describe: debian ? "the system CA bundle" : "the system trust anchors",
14870
- steps: debian ? [["sudo", [
14871
- "cp",
14872
- certificate,
14873
- "/usr/local/share/ca-certificates/gkm-local-ca.crt"
14874
- ]], ["sudo", ["update-ca-certificates"]]] : [["sudo", [
14875
- "cp",
14876
- certificate,
14877
- "/etc/pki/ca-trust/source/anchors/gkm-local-ca.crt"
14878
- ]], ["sudo", ["update-ca-trust"]]]
14879
- };
14966
+ //#region src/secrets/store.ts
14967
+ /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
14968
+ var FileSecretsStore = class {
14969
+ root;
14970
+ constructor(root) {
14971
+ this.root = root;
14880
14972
  }
14881
- throw new UnsupportedPlatform(platform, certificate);
14882
- }
14883
- /**
14884
- * Trust the certificate authority this project's edge issues from.
14885
- *
14886
- * Idempotent in effect: installing a root that is already trusted replaces the
14887
- * same entry, so running it twice is not an error.
14888
- */
14889
- async function trustCommand(options = {}) {
14890
- const { workspace } = await loadWorkspaceConfig();
14891
- const certificate = join(workspace.root, LOCAL_CA_PATH);
14892
- if (!existsSync(certificate)) throw new NoLocalAuthority(certificate);
14893
- const { describe, steps } = installation(process.platform, certificate);
14894
- logger$2.log(`\n🔐 Trusting the local authority in ${describe}`);
14895
- logger$2.log(` ${certificate}\n`);
14896
- if (options.dryRun) {
14897
- for (const [command, args] of steps) logger$2.log(` ${command} ${args.join(" ")}`);
14898
- return;
14973
+ pull(stage) {
14974
+ return readStageSecrets(stage, this.root);
14899
14975
  }
14900
- logger$2.log(" This needs an admin password.\n");
14901
- for (const [command, args] of steps) await run(command, args, { stdio: "inherit" });
14902
- logger$2.log("\n✅ Local https addresses are trusted. Restart your browser.\n");
14903
- }
14976
+ push(_stage, secrets) {
14977
+ return writeStageSecrets(secrets, this.root);
14978
+ }
14979
+ };
14904
14980
  /**
14905
- * Trust the authority if this machine lacks it and its owner agrees.
14906
- *
14907
- * A machine's answer, not the project's: whether a laptop installs a root
14908
- * certificate is its owner's call, so the decision is `--yes`, then what this
14909
- * machine previously answered, then a prompt. Never a silent `sudo` — that is
14910
- * the one thing a setup command should not do on somebody's behalf.
14981
+ * The store a stage's secrets live in.
14911
14982
  *
14912
- * Silent and free when the machine already trusts it, which is what makes it
14913
- * acceptable to call on every `gkm setup`.
14914
- */
14915
- async function ensureTrusted(root, url, options = {}) {
14916
- if (await isTrusted(url)) return;
14917
- if (!options.assumeYes) {
14918
- if (!process.stdin.isTTY) {
14919
- logger$2.log(" Local https addresses are not trusted here. Run \"gkm trust\", or \"gkm setup --yes\".");
14920
- return;
14921
- }
14922
- if (await declined(root)) return;
14923
- const { install } = await prompts({
14924
- type: "confirm",
14925
- name: "install",
14926
- message: "Trust this project's local certificate authority, so a browser accepts its https addresses? (needs sudo)",
14927
- initial: false
14928
- });
14929
- if (!install) {
14930
- await recordDeclined(root);
14931
- logger$2.log(" Skipped. Run \"gkm trust\" later.");
14932
- return;
14933
- }
14934
- }
14935
- await trustCommand();
14936
- }
14937
- /** Where a declined answer is remembered, so setup asks once and not again. */
14938
- const TRUST_STATE = ".gkm/trust.json";
14939
- async function declined(root) {
14940
- try {
14941
- const raw = await readFile(join(root, TRUST_STATE), "utf-8");
14942
- return JSON.parse(raw).declined === true;
14943
- } catch {
14944
- return false;
14945
- }
14983
+ * The local stage is always the file: its secrets belong to the machine
14984
+ * running `gkm dev`. A deployed stage uses `secrets.store`.
14985
+ */
14986
+ async function secretsStoreFor(workspace, stage, options = {}) {
14987
+ const configured = workspace.secrets.store ?? "file";
14988
+ if (stage === workspace.stages.local || configured === "file") return new FileSecretsStore(workspace.root);
14989
+ if (configured.provider !== "ssm") return configured.provider;
14990
+ const { SsmSecretsStore } = await import("./ssm-BKN1Cg4z.mjs");
14991
+ return new SsmSecretsStore({
14992
+ project: workspace.name,
14993
+ region: configured.region,
14994
+ ...options.profile ? { profile: options.profile } : {}
14995
+ });
14946
14996
  }
14947
- async function recordDeclined(root) {
14948
- const path = join(root, TRUST_STATE);
14949
- await mkdir(dirname(path), { recursive: true });
14950
- await writeFile(path, `${JSON.stringify({ declined: true }, null, 2)}\n`);
14997
+ /** Whether a stage's secrets live somewhere other than this machine. */
14998
+ function isRemoteStore(workspace, stage) {
14999
+ const configured = workspace.secrets.store ?? "file";
15000
+ return stage !== workspace.stages.local && configured !== "file";
14951
15001
  }
15002
+ //#endregion
15003
+ //#region src/secrets/transfer.ts
14952
15004
  /**
14953
- * Whether this machine already trusts the local authority.
14954
- *
14955
- * Asked by connecting rather than by reading a keychain: what matters is
14956
- * whether a client with no special configuration verifies the edge, and that is
14957
- * one platform-neutral question instead of three platform-specific ones. It is
14958
- * also the exact thing a browser is about to do.
15005
+ * Moving a deployed stage's secrets between this machine and its store.
14959
15006
  *
14960
- * A child process, because `NODE_EXTRA_CA_CERTS` is read once at startup and
14961
- * this process almost certainly has it set — asking in-process would answer
14962
- * "yes" for everyone.
14963
- */
14964
- async function isTrusted(url) {
14965
- const { NODE_EXTRA_CA_CERTS: _ignored, ...env } = process.env;
14966
- try {
14967
- await run(process.execPath, ["-e", `fetch(${JSON.stringify(url)}).then(()=>0,()=>process.exit(1))`], {
14968
- env,
14969
- timeout: 15e3
14970
- });
14971
- return true;
14972
- } catch {
14973
- return false;
15007
+ * `push` sends the encrypted local copy to the stage's store; `pull` brings it
15008
+ * back and writes the local copy a deploy reads — generating a key for it if
15009
+ * the machine has none, which is what a fresh CI runner needs. Both reconcile
15010
+ * the keys the workspace now derives, so a store never lags a new construct.
15011
+ */
15012
+ var transfer_exports = /* @__PURE__ */ __exportAll({
15013
+ NoLocalSecrets: () => NoLocalSecrets,
15014
+ NoRemoteSecretsStore: () => NoRemoteSecretsStore,
15015
+ NoStoredSecrets: () => NoStoredSecrets,
15016
+ pullStageSecrets: () => pullStageSecrets,
15017
+ pushStageSecrets: () => pushStageSecrets
15018
+ });
15019
+ /** A push or pull for a stage with nowhere to push or pull from. */
15020
+ var NoRemoteSecretsStore = class extends Error {
15021
+ stage;
15022
+ constructor(stage) {
15023
+ super(`Stage "${stage}" keeps its secrets in the local file, so there is nothing to push or pull. Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.`);
15024
+ this.stage = stage;
15025
+ this.name = "NoRemoteSecretsStore";
14974
15026
  }
14975
- }
14976
- /** The edge has not run, so there is no authority to trust yet. */
14977
- var NoLocalAuthority = class extends Error {
14978
- path;
14979
- constructor(path) {
14980
- super(`No local certificate authority at ${path}. It is generated the first time the edge starts, so run "gkm setup" (or "gkm dev") once — and if this project declares no file server, nothing needs one.`);
14981
- this.path = path;
14982
- this.name = "NoLocalAuthority";
15027
+ };
15028
+ /** A push for a stage whose secrets this machine does not have. */
15029
+ var NoLocalSecrets = class extends Error {
15030
+ stage;
15031
+ constructor(stage) {
15032
+ super(`No secrets for stage "${stage}" on this machine. Run gkm secrets:init --stage ${stage} first.`);
15033
+ this.stage = stage;
15034
+ this.name = "NoLocalSecrets";
14983
15035
  }
14984
15036
  };
14985
- /** A platform with no trust store this knows how to write to. */
14986
- var UnsupportedPlatform = class extends Error {
14987
- platform;
14988
- certificate;
14989
- constructor(platform, certificate) {
14990
- super(`gkm does not know how to install a certificate authority on ${platform}. The root is at ${certificate} — add it to the trust store by hand, or point a client at it with NODE_EXTRA_CA_CERTS.`);
14991
- this.platform = platform;
14992
- this.certificate = certificate;
14993
- this.name = "UnsupportedPlatform";
15037
+ /** A pull for a stage the store holds nothing for. */
15038
+ var NoStoredSecrets = class extends Error {
15039
+ stage;
15040
+ constructor(stage) {
15041
+ super(`The store holds no secrets for stage "${stage}". Push them first: gkm secrets:push --stage ${stage}.`);
15042
+ this.stage = stage;
15043
+ this.name = "NoStoredSecrets";
14994
15044
  }
14995
15045
  };
15046
+ async function reconcile(secrets, workspace, stage) {
15047
+ const { derivedContainers } = await import("./workspace-cBobQVoA.mjs").then((n) => n.a);
15048
+ const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
15049
+ return result ? {
15050
+ secrets: result.secrets,
15051
+ addedKeys: result.addedKeys
15052
+ } : {
15053
+ secrets,
15054
+ addedKeys: []
15055
+ };
15056
+ }
15057
+ function remoteOnly(workspace, stage) {
15058
+ assertDeployedStage(workspace.stages, stage);
15059
+ if (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);
15060
+ }
15061
+ /** Send this machine's secrets for a deployed stage to its store. */
15062
+ async function pushStageSecrets(workspace, stage, options = {}) {
15063
+ remoteOnly(workspace, stage);
15064
+ const local = await readStageSecrets(stage, workspace.root);
15065
+ if (!local) throw new NoLocalSecrets(stage);
15066
+ const result = await reconcile(local, workspace, stage);
15067
+ if (result.addedKeys.length) await writeStageSecrets(result.secrets, workspace.root);
15068
+ await (await secretsStoreFor(workspace, stage, options)).push(stage, result.secrets);
15069
+ return result;
15070
+ }
15071
+ /** Bring a deployed stage's secrets from its store into the local copy. */
15072
+ async function pullStageSecrets(workspace, stage, options = {}) {
15073
+ remoteOnly(workspace, stage);
15074
+ const stored = await (await secretsStoreFor(workspace, stage, options)).pull(stage);
15075
+ if (!stored) throw new NoStoredSecrets(stage);
15076
+ const result = await reconcile(stored, workspace, stage);
15077
+ await writeStageSecrets(result.secrets, workspace.root);
15078
+ return result;
15079
+ }
14996
15080
  //#endregion
14997
15081
  //#region src/setup/index.ts
14998
15082
  var setup_exports = /* @__PURE__ */ __exportAll({
@@ -15061,8 +15145,7 @@ async function reconcileLocal(workspace, stage, options) {
15061
15145
  }
15062
15146
  logger$1.log(`🐳 Services: ${result.plan.containers.join(", ")}`);
15063
15147
  for (const [container, address] of Object.entries(result.addresses)) logger$1.log(` ${container}: ${address}`);
15064
- const served = result.plan.resources.find((r) => r.kind === "file-server");
15065
- const url = served ? result.env[served.envKey] : void 0;
15148
+ const url = result.plan.resources.filter((r) => r.kind === "rest-api" || r.kind === "site" || r.kind === "file-server").map((r) => result.env[r.envKey]).find((address) => address?.startsWith("https://"));
15066
15149
  if (url) await ensureTrusted(workspace.root, url, { ...options.yes ? { assumeYes: true } : {} });
15067
15150
  }
15068
15151
  /**
@@ -16109,7 +16192,7 @@ program.command("secrets:push").description("Send a deployed stage's secrets to
16109
16192
  const globalOptions = program.opts();
16110
16193
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16111
16194
  const { loadWorkspaceConfig } = await import("./config.mjs");
16112
- const { pushStageSecrets } = await import("./transfer-BV00TxZv.mjs").then((n) => n.i);
16195
+ const { pushStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16113
16196
  const { workspace } = await loadWorkspaceConfig();
16114
16197
  const { addedKeys } = await pushStageSecrets(workspace, options.stage, { profile: options.profile });
16115
16198
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16124,7 +16207,7 @@ program.command("secrets:pull").description("Bring a deployed stage's secrets fr
16124
16207
  const globalOptions = program.opts();
16125
16208
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16126
16209
  const { loadWorkspaceConfig } = await import("./config.mjs");
16127
- const { pullStageSecrets } = await import("./transfer-BV00TxZv.mjs").then((n) => n.i);
16210
+ const { pullStageSecrets } = await Promise.resolve().then(() => transfer_exports);
16128
16211
  const { workspace } = await loadWorkspaceConfig();
16129
16212
  const { addedKeys } = await pullStageSecrets(workspace, options.stage, { profile: options.profile });
16130
16213
  for (const key of addedKeys) console.log(` + ${key} (reconciled)`);
@@ -16148,7 +16231,7 @@ program.command("secrets:reconcile").description("Backfill missing custom secret
16148
16231
  console.error(`No secrets found for stage "${stage}". Run "gkm secrets:init --stage ${stage}" first.`);
16149
16232
  process.exit(1);
16150
16233
  }
16151
- const { derivedContainers } = await import("./workspace-CUmR_k4J.mjs").then((n) => n.a);
16234
+ const { derivedContainers } = await import("./workspace-cBobQVoA.mjs").then((n) => n.a);
16152
16235
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
16153
16236
  if (!result) {
16154
16237
  console.log(`\n✓ Secrets for stage "${stage}" are up-to-date`);
@@ -16207,7 +16290,7 @@ program.command("deploy:github").description("Let GitHub Actions deploy a stage:
16207
16290
  try {
16208
16291
  const globalOptions = program.opts();
16209
16292
  if (globalOptions.cwd) process.chdir(globalOptions.cwd);
16210
- const { deployGithubCommand } = await import("./github-Ba5kXEJo.mjs");
16293
+ const { deployGithubCommand } = await import("./github-zkzu-ka2.mjs");
16211
16294
  await deployGithubCommand(options);
16212
16295
  } catch (error) {
16213
16296
  console.error(formatError(error));
@@ -16332,6 +16415,6 @@ program.command("upgrade").description("Upgrade @geekmidas/cli (with --all, ever
16332
16415
  });
16333
16416
  program.parse();
16334
16417
  //#endregion
16335
- export { getHostingerToken as n, encryption_exports as t };
16418
+ export { getHostingerToken as i, isRemoteStore as n, encryption_exports as r, pushStageSecrets as t };
16336
16419
 
16337
16420
  //# sourceMappingURL=index.mjs.map