@geekmidas/cli 10.0.0-alpha.17 → 10.0.0-alpha.19

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 (42) hide show
  1. package/dist/{config-CdTMeMU_.cjs → config-DSXI480r.cjs} +2 -2
  2. package/dist/{config-CdTMeMU_.cjs.map → config-DSXI480r.cjs.map} +1 -1
  3. package/dist/{config-DPnPkWyH.mjs → config-DdKkoUE1.mjs} +2 -2
  4. package/dist/{config-DPnPkWyH.mjs.map → config-DdKkoUE1.mjs.map} +1 -1
  5. package/dist/config.cjs +2 -2
  6. package/dist/config.mjs +2 -2
  7. package/dist/{credentials-tvyReUAd.cjs → credentials-C6Pfn6GY.cjs} +30 -8
  8. package/dist/credentials-C6Pfn6GY.cjs.map +1 -0
  9. package/dist/{credentials-fJP5-fa1.mjs → credentials-lbv-j0Be.mjs} +31 -9
  10. package/dist/{credentials-fJP5-fa1.mjs.map → credentials-lbv-j0Be.mjs.map} +1 -1
  11. package/dist/{github-DVE8Gg_F.cjs → github-BvfpvBcW.cjs} +4 -4
  12. package/dist/{github-DVE8Gg_F.cjs.map → github-BvfpvBcW.cjs.map} +1 -1
  13. package/dist/{github-CAfKyYPN.mjs → github-DW3CP-4D.mjs} +4 -4
  14. package/dist/{github-CAfKyYPN.mjs.map → github-DW3CP-4D.mjs.map} +1 -1
  15. package/dist/index.cjs +78 -55
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.mjs +78 -55
  18. package/dist/index.mjs.map +1 -1
  19. package/dist/{openapi-BeckoaP9.cjs → openapi-B8bu_OXN.cjs} +2 -2
  20. package/dist/{openapi-BeckoaP9.cjs.map → openapi-B8bu_OXN.cjs.map} +1 -1
  21. package/dist/{openapi-DolhBm6r.mjs → openapi-DfjbhrsM.mjs} +2 -2
  22. package/dist/{openapi-DolhBm6r.mjs.map → openapi-DfjbhrsM.mjs.map} +1 -1
  23. package/dist/openapi.cjs +1 -1
  24. package/dist/openapi.mjs +1 -1
  25. package/dist/{reconcile-fmoluZmZ.mjs → reconcile-gb1lz1Dk.mjs} +3 -3
  26. package/dist/reconcile-gb1lz1Dk.mjs.map +1 -0
  27. package/dist/{reconcile-Cq4cO83R.cjs → reconcile-hx_vBvux.cjs} +3 -3
  28. package/dist/reconcile-hx_vBvux.cjs.map +1 -0
  29. package/dist/{transfer-4A4S2hcw.mjs → transfer-DjFZRgub.mjs} +4 -4
  30. package/dist/{transfer-4A4S2hcw.mjs.map → transfer-DjFZRgub.mjs.map} +1 -1
  31. package/dist/{transfer-DdmtKLzi.cjs → transfer-SeiSeKEF.cjs} +4 -4
  32. package/dist/{transfer-DdmtKLzi.cjs.map → transfer-SeiSeKEF.cjs.map} +1 -1
  33. package/dist/workspace/index.cjs +1 -1
  34. package/dist/workspace/index.mjs +1 -1
  35. package/dist/{workspace-DI-M3BnN.mjs → workspace-Bu3xNCej.mjs} +2 -2
  36. package/dist/{workspace-DI-M3BnN.mjs.map → workspace-Bu3xNCej.mjs.map} +1 -1
  37. package/dist/{workspace-B8NLb54N.cjs → workspace-CRD9YinV.cjs} +7 -1
  38. package/dist/{workspace-B8NLb54N.cjs.map → workspace-CRD9YinV.cjs.map} +1 -1
  39. package/package.json +11 -11
  40. package/dist/credentials-tvyReUAd.cjs.map +0 -1
  41. package/dist/reconcile-Cq4cO83R.cjs.map +0 -1
  42. package/dist/reconcile-fmoluZmZ.mjs.map +0 -1
package/dist/openapi.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env -S npx tsx
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const require_openapi = require("./openapi-BeckoaP9.cjs");
3
+ const require_openapi = require("./openapi-B8bu_OXN.cjs");
4
4
  exports.OPENAPI_OUTPUT_PATH = require_openapi.OPENAPI_OUTPUT_PATH;
5
5
  exports.generateOpenApi = require_openapi.generateOpenApi;
6
6
  exports.generateOpenApiFrom = require_openapi.generateOpenApiFrom;
package/dist/openapi.mjs CHANGED
@@ -1,3 +1,3 @@
1
1
  #!/usr/bin/env -S npx tsx
2
- import { a as openapiCommand, i as openApiPathFor, n as generateOpenApi, o as resolveOpenApiConfig, r as generateOpenApiFrom, t as OPENAPI_OUTPUT_PATH } from "./openapi-DolhBm6r.mjs";
2
+ import { a as openapiCommand, i as openApiPathFor, n as generateOpenApi, o as resolveOpenApiConfig, r as generateOpenApiFrom, t as OPENAPI_OUTPUT_PATH } from "./openapi-DfjbhrsM.mjs";
3
3
  export { OPENAPI_OUTPUT_PATH, generateOpenApi, generateOpenApiFrom, openApiPathFor, openapiCommand, resolveOpenApiConfig };
@@ -21,14 +21,14 @@ function generateDbUrl(appName, password, projectName, host = "localhost", port
21
21
  * Generate fullstack-aware custom secrets for a workspace.
22
22
  *
23
23
  * Generates:
24
- * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
24
+ * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command
25
+ * decides that; see `gkm init`)
25
26
  * - Per-app database passwords and URLs for backend apps with db service
26
27
  * - Better-auth secrets for apps using the better-auth framework
27
28
  */
28
29
  function generateFullstackCustomSecrets(workspace, containers = []) {
29
30
  const hasDb = containers.includes("postgres");
30
31
  const customs = {
31
- NODE_ENV: "development",
32
32
  PORT: "3000",
33
33
  LOG_LEVEL: "debug",
34
34
  JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
@@ -110,4 +110,4 @@ function reconcileMissingSecrets(secrets, workspace, containers = []) {
110
110
  //#endregion
111
111
  export { writeDockerEnvFromSecrets as i, reconcile_exports as n, generateFullstackCustomSecrets as r, reconcileMissingSecrets as t };
112
112
 
113
- //# sourceMappingURL=reconcile-fmoluZmZ.mjs.map
113
+ //# sourceMappingURL=reconcile-gb1lz1Dk.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-gb1lz1Dk.mjs","names":[],"sources":["../src/setup/fullstack-secrets.ts","../src/secrets/reconcile.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { generateSecurePassword } from '../secrets/generator.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/**\n * Generate a secure random password for database users.\n * Uses a combination of timestamp and random bytes for uniqueness.\n */\nexport function generateDbPassword(): string {\n\treturn `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Generate database URL for an app.\n * All apps connect to the same database, but use different users/schemas.\n */\nexport function generateDbUrl(\n\tappName: string,\n\tpassword: string,\n\tprojectName: string,\n\thost = 'localhost',\n\tport = 5432,\n): string {\n\tconst userName = appName.replace(/-/g, '_');\n\tconst dbName = `${projectName.replace(/-/g, '_')}_dev`;\n\treturn `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;\n}\n\n/**\n * Generate fullstack-aware custom secrets for a workspace.\n *\n * Generates:\n * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command\n * decides that; see `gkm init`)\n * - Per-app database passwords and URLs for backend apps with db service\n * - Better-auth secrets for apps using the better-auth framework\n */\nexport function generateFullstackCustomSecrets(\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): Record<string, string> {\n\t// A Postgres exists because something declared a database, not because\n\t// config carried a flag saying so.\n\tconst hasDb = containers.includes('postgres');\n\tconst customs: Record<string, string> = {\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\tif (!hasDb) {\n\t\treturn customs;\n\t}\n\n\t// Collect all frontend ports for trusted origins\n\tconst frontendPorts: number[] = [];\n\n\tfor (const [appName, appConfig] of Object.entries(workspace.apps)) {\n\t\tif (appConfig.type === 'web') {\n\t\t\tfrontendPorts.push(appConfig.port);\n\t\t\tconst upperName = appName.toUpperCase();\n\t\t\tcustoms[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Mobile apps (Expo, etc.) don't run on a host port the backend\n\t\t// cares about for CORS, and don't need DB credentials.\n\t\tif (appConfig.type === 'mobile') {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Backend apps with database: generate per-app DB passwords and URLs\n\t\tconst password = generateDbPassword();\n\t\tconst upperName = appName.toUpperCase();\n\n\t\tcustoms[`${upperName}_DATABASE_URL`] = generateDbUrl(\n\t\t\tappName,\n\t\t\tpassword,\n\t\t\tworkspace.name,\n\t\t);\n\t\tcustoms[`${upperName}_DB_PASSWORD`] = password;\n\n\t\t// Better-auth framework secrets\n\t\tif (appConfig.framework === 'better-auth') {\n\t\t\tcustoms.AUTH_PORT = String(appConfig.port);\n\t\t\tcustoms.AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t\tcustoms.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;\n\t\t\tcustoms.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t}\n\t}\n\n\t// Generate trusted origins for better-auth (all app ports)\n\tif (customs.BETTER_AUTH_SECRET) {\n\t\tconst allPorts = Object.values(workspace.apps).map((a) => a.port);\n\t\tcustoms.BETTER_AUTH_TRUSTED_ORIGINS = allPorts\n\t\t\t.map((p) => `http://localhost:${p}`)\n\t\t\t.join(',');\n\t}\n\n\treturn customs;\n}\n\n/**\n * Extract *_DB_PASSWORD keys from secrets and write docker/.env.\n *\n * The docker/.env file contains database passwords that the PostgreSQL\n * init script reads to create per-app database users.\n */\nexport async function writeDockerEnvFromSecrets(\n\tsecrets: StageSecrets,\n\tworkspaceRoot: string,\n): Promise<void> {\n\tconst dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) =>\n\t\tkey.endsWith('_DB_PASSWORD'),\n\t);\n\n\t// Always include pgboss password when pgboss credentials exist\n\tif (\n\t\tsecrets.services.pgboss &&\n\t\t!dbPasswordEntries.some(([key]) => key === 'PGBOSS_DB_PASSWORD')\n\t) {\n\t\tdbPasswordEntries.push([\n\t\t\t'PGBOSS_DB_PASSWORD',\n\t\t\tsecrets.services.pgboss.password,\n\t\t]);\n\t}\n\n\tif (dbPasswordEntries.length === 0) {\n\t\treturn;\n\t}\n\n\tconst envContent = `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join('\\n')}\n`;\n\n\tconst envPath = join(workspaceRoot, 'docker', '.env');\n\tawait mkdir(dirname(envPath), { recursive: true });\n\tawait writeFile(envPath, envContent);\n}\n","import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace, containers);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":"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"}
@@ -21,14 +21,14 @@ function generateDbUrl(appName, password, projectName, host = "localhost", port
21
21
  * Generate fullstack-aware custom secrets for a workspace.
22
22
  *
23
23
  * Generates:
24
- * - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
24
+ * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command
25
+ * decides that; see `gkm init`)
25
26
  * - Per-app database passwords and URLs for backend apps with db service
26
27
  * - Better-auth secrets for apps using the better-auth framework
27
28
  */
28
29
  function generateFullstackCustomSecrets(workspace, containers = []) {
29
30
  const hasDb = containers.includes("postgres");
30
31
  const customs = {
31
- NODE_ENV: "development",
32
32
  PORT: "3000",
33
33
  LOG_LEVEL: "debug",
34
34
  JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
@@ -133,4 +133,4 @@ Object.defineProperty(exports, "writeDockerEnvFromSecrets", {
133
133
  }
134
134
  });
135
135
 
136
- //# sourceMappingURL=reconcile-Cq4cO83R.cjs.map
136
+ //# sourceMappingURL=reconcile-hx_vBvux.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reconcile-hx_vBvux.cjs","names":["generateSecurePassword","join","mkdir","dirname","writeFile"],"sources":["../src/setup/fullstack-secrets.ts","../src/secrets/reconcile.ts"],"sourcesContent":["import { mkdir, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { generateSecurePassword } from '../secrets/generator.js';\nimport type { StageSecrets } from '../secrets/types.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\n\n/**\n * Generate a secure random password for database users.\n * Uses a combination of timestamp and random bytes for uniqueness.\n */\nexport function generateDbPassword(): string {\n\treturn `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;\n}\n\n/**\n * Generate database URL for an app.\n * All apps connect to the same database, but use different users/schemas.\n */\nexport function generateDbUrl(\n\tappName: string,\n\tpassword: string,\n\tprojectName: string,\n\thost = 'localhost',\n\tport = 5432,\n): string {\n\tconst userName = appName.replace(/-/g, '_');\n\tconst dbName = `${projectName.replace(/-/g, '_')}_dev`;\n\treturn `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;\n}\n\n/**\n * Generate fullstack-aware custom secrets for a workspace.\n *\n * Generates:\n * - Common secrets: PORT, LOG_LEVEL, JWT_SECRET (no `NODE_ENV` — the command\n * decides that; see `gkm init`)\n * - Per-app database passwords and URLs for backend apps with db service\n * - Better-auth secrets for apps using the better-auth framework\n */\nexport function generateFullstackCustomSecrets(\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): Record<string, string> {\n\t// A Postgres exists because something declared a database, not because\n\t// config carried a flag saying so.\n\tconst hasDb = containers.includes('postgres');\n\tconst customs: Record<string, string> = {\n\t\tPORT: '3000',\n\t\tLOG_LEVEL: 'debug',\n\t\tJWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`,\n\t};\n\n\tif (!hasDb) {\n\t\treturn customs;\n\t}\n\n\t// Collect all frontend ports for trusted origins\n\tconst frontendPorts: number[] = [];\n\n\tfor (const [appName, appConfig] of Object.entries(workspace.apps)) {\n\t\tif (appConfig.type === 'web') {\n\t\t\tfrontendPorts.push(appConfig.port);\n\t\t\tconst upperName = appName.toUpperCase();\n\t\t\tcustoms[`${upperName}_URL`] = `http://localhost:${appConfig.port}`;\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Mobile apps (Expo, etc.) don't run on a host port the backend\n\t\t// cares about for CORS, and don't need DB credentials.\n\t\tif (appConfig.type === 'mobile') {\n\t\t\tcontinue;\n\t\t}\n\n\t\t// Backend apps with database: generate per-app DB passwords and URLs\n\t\tconst password = generateDbPassword();\n\t\tconst upperName = appName.toUpperCase();\n\n\t\tcustoms[`${upperName}_DATABASE_URL`] = generateDbUrl(\n\t\t\tappName,\n\t\t\tpassword,\n\t\t\tworkspace.name,\n\t\t);\n\t\tcustoms[`${upperName}_DB_PASSWORD`] = password;\n\n\t\t// Better-auth framework secrets\n\t\tif (appConfig.framework === 'better-auth') {\n\t\t\tcustoms.AUTH_PORT = String(appConfig.port);\n\t\t\tcustoms.AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t\tcustoms.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;\n\t\t\tcustoms.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;\n\t\t}\n\t}\n\n\t// Generate trusted origins for better-auth (all app ports)\n\tif (customs.BETTER_AUTH_SECRET) {\n\t\tconst allPorts = Object.values(workspace.apps).map((a) => a.port);\n\t\tcustoms.BETTER_AUTH_TRUSTED_ORIGINS = allPorts\n\t\t\t.map((p) => `http://localhost:${p}`)\n\t\t\t.join(',');\n\t}\n\n\treturn customs;\n}\n\n/**\n * Extract *_DB_PASSWORD keys from secrets and write docker/.env.\n *\n * The docker/.env file contains database passwords that the PostgreSQL\n * init script reads to create per-app database users.\n */\nexport async function writeDockerEnvFromSecrets(\n\tsecrets: StageSecrets,\n\tworkspaceRoot: string,\n): Promise<void> {\n\tconst dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) =>\n\t\tkey.endsWith('_DB_PASSWORD'),\n\t);\n\n\t// Always include pgboss password when pgboss credentials exist\n\tif (\n\t\tsecrets.services.pgboss &&\n\t\t!dbPasswordEntries.some(([key]) => key === 'PGBOSS_DB_PASSWORD')\n\t) {\n\t\tdbPasswordEntries.push([\n\t\t\t'PGBOSS_DB_PASSWORD',\n\t\t\tsecrets.services.pgboss.password,\n\t\t]);\n\t}\n\n\tif (dbPasswordEntries.length === 0) {\n\t\treturn;\n\t}\n\n\tconst envContent = `# Auto-generated docker environment file\n# Contains database passwords for docker-compose postgres init\n# This file is gitignored - do not commit to version control\n${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join('\\n')}\n`;\n\n\tconst envPath = join(workspaceRoot, 'docker', '.env');\n\tawait mkdir(dirname(envPath), { recursive: true });\n\tawait writeFile(envPath, envContent);\n}\n","import { generateFullstackCustomSecrets } from '../setup/fullstack-secrets.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface ReconcileResult {\n\t/** The updated secrets with missing keys backfilled */\n\tsecrets: StageSecrets;\n\t/** Keys that were added */\n\taddedKeys: string[];\n}\n\n/**\n * Reconcile missing custom secrets for a workspace.\n *\n * Compares current secrets against what generateFullstackCustomSecrets()\n * would produce and backfills any missing keys without overwriting\n * existing values.\n *\n * @returns ReconcileResult if keys were added, null if secrets are up-to-date\n */\nexport function reconcileMissingSecrets(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tcontainers: readonly string[] = [],\n): ReconcileResult | null {\n\tconst isMultiApp = Object.keys(workspace.apps).length > 1;\n\tif (!isMultiApp) {\n\t\treturn null;\n\t}\n\n\tconst expectedCustom = generateFullstackCustomSecrets(workspace, containers);\n\tconst addedKeys: string[] = [];\n\tconst mergedCustom = { ...secrets.custom };\n\n\tfor (const [key, value] of Object.entries(expectedCustom)) {\n\t\tif (!(key in mergedCustom)) {\n\t\t\tmergedCustom[key] = value;\n\t\t\taddedKeys.push(key);\n\t\t}\n\t}\n\n\tif (addedKeys.length === 0) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tsecrets: {\n\t\t\t...secrets,\n\t\t\tupdatedAt: new Date().toISOString(),\n\t\t\tcustom: mergedCustom,\n\t\t},\n\t\taddedKeys,\n\t};\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-BhDjJH2R.mjs";
2
- import { w as assertDeployedStage } from "./workspace-DI-M3BnN.mjs";
2
+ import { T as assertDeployedStage } from "./workspace-Bu3xNCej.mjs";
3
3
  import { n as readStageSecrets, s as writeStageSecrets } from "./storage-TP0x118M.mjs";
4
- import { t as reconcileMissingSecrets } from "./reconcile-fmoluZmZ.mjs";
4
+ import { t as reconcileMissingSecrets } from "./reconcile-gb1lz1Dk.mjs";
5
5
  //#region src/secrets/store.ts
6
6
  /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
7
7
  var FileSecretsStore = class {
@@ -83,7 +83,7 @@ var NoStoredSecrets = class extends Error {
83
83
  }
84
84
  };
85
85
  async function reconcile(secrets, workspace, stage) {
86
- const { derivedContainers } = await import("./credentials-fJP5-fa1.mjs").then((n) => n.h);
86
+ const { derivedContainers } = await import("./credentials-lbv-j0Be.mjs").then((n) => n.h);
87
87
  const result = reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
88
88
  return result ? {
89
89
  secrets: result.secrets,
@@ -119,4 +119,4 @@ async function pullStageSecrets(workspace, stage, options = {}) {
119
119
  //#endregion
120
120
  export { isRemoteStore as a, transfer_exports as i, pullStageSecrets as n, pushStageSecrets as r, NoStoredSecrets as t };
121
121
 
122
- //# sourceMappingURL=transfer-4A4S2hcw.mjs.map
122
+ //# sourceMappingURL=transfer-DjFZRgub.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer-4A4S2hcw.mjs","names":[],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transfer-DjFZRgub.mjs","names":[],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  const require_rolldown_runtime = require("./rolldown-runtime-C0BPl7ul.cjs");
2
- const require_workspace = require("./workspace-B8NLb54N.cjs");
2
+ const require_workspace = require("./workspace-CRD9YinV.cjs");
3
3
  const require_storage = require("./storage-DgypfSD6.cjs");
4
- const require_reconcile = require("./reconcile-Cq4cO83R.cjs");
4
+ const require_reconcile = require("./reconcile-hx_vBvux.cjs");
5
5
  //#region src/secrets/store.ts
6
6
  /** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */
7
7
  var FileSecretsStore = class {
@@ -83,7 +83,7 @@ var NoStoredSecrets = class extends Error {
83
83
  }
84
84
  };
85
85
  async function reconcile(secrets, workspace, stage) {
86
- const { derivedContainers } = await Promise.resolve().then(() => require("./credentials-tvyReUAd.cjs")).then((n) => n.workspace_exports);
86
+ const { derivedContainers } = await Promise.resolve().then(() => require("./credentials-C6Pfn6GY.cjs")).then((n) => n.workspace_exports);
87
87
  const result = require_reconcile.reconcileMissingSecrets(secrets, workspace, await derivedContainers(workspace, stage));
88
88
  return result ? {
89
89
  secrets: result.secrets,
@@ -148,4 +148,4 @@ Object.defineProperty(exports, "transfer_exports", {
148
148
  }
149
149
  });
150
150
 
151
- //# sourceMappingURL=transfer-DdmtKLzi.cjs.map
151
+ //# sourceMappingURL=transfer-SeiSeKEF.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transfer-DdmtKLzi.cjs","names":["readStageSecrets","writeStageSecrets","reconcileMissingSecrets","readStageSecrets","writeStageSecrets"],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"transfer-SeiSeKEF.cjs","names":["readStageSecrets","writeStageSecrets","reconcileMissingSecrets","readStageSecrets","writeStageSecrets"],"sources":["../src/secrets/store.ts","../src/secrets/transfer.ts"],"sourcesContent":["/**\n * Where a stage's secrets live when they are not only on one machine.\n *\n * A deployed stage has to reach its secrets from wherever it is deployed from\n * — a teammate's laptop today, a CI runner tomorrow — and `.gkm/` is not\n * committed, so the encrypted file on the machine that created them is not a\n * home for them. A store is: `push` puts a stage's secrets there, `pull`\n * brings them back, and the deploy reads the local copy `pull` writes.\n *\n * The same shape deploy state has (`StateProvider`): one interface, a backend\n * per place, and a custom object for any place this package does not ship.\n */\n\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface SecretsStore {\n\t/** The stage's secrets, or null if the store holds none for it. */\n\tpull(stage: string): Promise<StageSecrets | null>;\n\t/** Replace the stage's secrets in the store. */\n\tpush(stage: string, secrets: StageSecrets): Promise<void>;\n}\n\n/** Secrets in SSM Parameter Store, in the account of the active credentials. */\nexport interface SsmSecretsStoreConfig {\n\tprovider: 'ssm';\n\tregion: string;\n}\n\n/** Any other backend: an object implementing {@link SecretsStore}. */\nexport interface CustomSecretsStoreConfig {\n\tprovider: SecretsStore;\n}\n\n/**\n * `secrets.store` in gkm.config.ts.\n *\n * `'file'` — the default — keeps each stage's secrets in the encrypted\n * `.gkm/secrets/<stage>.json` beside its key in `~/.gkm/`. It cannot serve a\n * deploy from CI while `.gkm/` is gitignored; `ssm` or a custom store can.\n */\nexport type SecretsStoreConfig =\n\t| 'file'\n\t| SsmSecretsStoreConfig\n\t| CustomSecretsStoreConfig;\n\n/** The encrypted file under `.gkm/secrets/`, and its key under `~/.gkm/`. */\nexport class FileSecretsStore implements SecretsStore {\n\tconstructor(private readonly root: string) {}\n\n\tpull(stage: string): Promise<StageSecrets | null> {\n\t\treturn readStageSecrets(stage, this.root);\n\t}\n\n\tpush(_stage: string, secrets: StageSecrets): Promise<void> {\n\t\treturn writeStageSecrets(secrets, this.root);\n\t}\n}\n\nexport interface SecretsStoreOptions {\n\t/**\n\t * The AWS profile for the stage's account. Only that profile is used —\n\t * never AWS_* from the environment — so a store cannot quietly land in\n\t * whichever account the shell was exported for.\n\t */\n\tprofile?: string;\n}\n\n/**\n * The store a stage's secrets live in.\n *\n * The local stage is always the file: its secrets belong to the machine\n * running `gkm dev`. A deployed stage uses `secrets.store`.\n */\nexport async function secretsStoreFor(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<SecretsStore> {\n\tconst configured = workspace.secrets.store ?? 'file';\n\n\tif (stage === workspace.stages.local || configured === 'file') {\n\t\treturn new FileSecretsStore(workspace.root);\n\t}\n\n\tif (configured.provider !== 'ssm') {\n\t\treturn configured.provider;\n\t}\n\n\tconst { SsmSecretsStore } = await import('./ssm.js');\n\treturn new SsmSecretsStore({\n\t\tproject: workspace.name,\n\t\tregion: configured.region,\n\t\t...(options.profile ? { profile: options.profile } : {}),\n\t});\n}\n\n/** Whether a stage's secrets live somewhere other than this machine. */\nexport function isRemoteStore(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): boolean {\n\tconst configured = workspace.secrets.store ?? 'file';\n\treturn stage !== workspace.stages.local && configured !== 'file';\n}\n","/**\n * Moving a deployed stage's secrets between this machine and its store.\n *\n * `push` sends the encrypted local copy to the stage's store; `pull` brings it\n * back and writes the local copy a deploy reads — generating a key for it if\n * the machine has none, which is what a fresh CI runner needs. Both reconcile\n * the keys the workspace now derives, so a store never lags a new construct.\n */\n\nimport { assertDeployedStage } from '../workspace/stages.js';\nimport type { NormalizedWorkspace } from '../workspace/types.js';\nimport { reconcileMissingSecrets } from './reconcile.js';\nimport { readStageSecrets, writeStageSecrets } from './storage.js';\nimport {\n\tisRemoteStore,\n\ttype SecretsStoreOptions,\n\tsecretsStoreFor,\n} from './store.js';\nimport type { StageSecrets } from './types.js';\n\nexport interface TransferResult {\n\tsecrets: StageSecrets;\n\t/** Keys the workspace derives that the secrets were missing, now added. */\n\taddedKeys: string[];\n}\n\n/** A push or pull for a stage with nowhere to push or pull from. */\nexport class NoRemoteSecretsStore extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`Stage \"${stage}\" keeps its secrets in the local file, so there is nothing to push or pull. ` +\n\t\t\t\t\"Set secrets.store in gkm.config.ts (e.g. { provider: 'ssm', region: 'eu-west-1' }) for deployed stages.\",\n\t\t);\n\t\tthis.name = 'NoRemoteSecretsStore';\n\t}\n}\n\n/** A push for a stage whose secrets this machine does not have. */\nexport class NoLocalSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`No secrets for stage \"${stage}\" on this machine. Run gkm secrets:init --stage ${stage} first.`,\n\t\t);\n\t\tthis.name = 'NoLocalSecrets';\n\t}\n}\n\n/** A pull for a stage the store holds nothing for. */\nexport class NoStoredSecrets extends Error {\n\tconstructor(readonly stage: string) {\n\t\tsuper(\n\t\t\t`The store holds no secrets for stage \"${stage}\". Push them first: gkm secrets:push --stage ${stage}.`,\n\t\t);\n\t\tthis.name = 'NoStoredSecrets';\n\t}\n}\n\nasync function reconcile(\n\tsecrets: StageSecrets,\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n): Promise<TransferResult> {\n\tconst { derivedContainers } = await import('../reconcile/workspace.js');\n\tconst result = reconcileMissingSecrets(\n\t\tsecrets,\n\t\tworkspace,\n\t\tawait derivedContainers(workspace, stage),\n\t);\n\treturn result\n\t\t? { secrets: result.secrets, addedKeys: result.addedKeys }\n\t\t: { secrets, addedKeys: [] };\n}\n\nfunction remoteOnly(workspace: NormalizedWorkspace, stage: string): void {\n\tassertDeployedStage(workspace.stages, stage);\n\tif (!isRemoteStore(workspace, stage)) throw new NoRemoteSecretsStore(stage);\n}\n\n/** Send this machine's secrets for a deployed stage to its store. */\nexport async function pushStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst local = await readStageSecrets(stage, workspace.root);\n\tif (!local) throw new NoLocalSecrets(stage);\n\n\tconst result = await reconcile(local, workspace, stage);\n\tif (result.addedKeys.length) {\n\t\tawait writeStageSecrets(result.secrets, workspace.root);\n\t}\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tawait store.push(stage, result.secrets);\n\treturn result;\n}\n\n/** Bring a deployed stage's secrets from its store into the local copy. */\nexport async function pullStageSecrets(\n\tworkspace: NormalizedWorkspace,\n\tstage: string,\n\toptions: SecretsStoreOptions = {},\n): Promise<TransferResult> {\n\tremoteOnly(workspace, stage);\n\n\tconst store = await secretsStoreFor(workspace, stage, options);\n\tconst stored = await store.pull(stage);\n\tif (!stored) throw new NoStoredSecrets(stage);\n\n\tconst result = await reconcile(stored, workspace, stage);\n\tawait writeStageSecrets(result.secrets, workspace.root);\n\treturn result;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_workspace = require("../workspace-B8NLb54N.cjs");
2
+ const require_workspace = require("../workspace-CRD9YinV.cjs");
3
3
  exports.PHASE_2_DEPLOY_TARGETS = require_workspace.PHASE_2_DEPLOY_TARGETS;
4
4
  exports.PUBLIC_ENV_PREFIXES = require_workspace.PUBLIC_ENV_PREFIXES;
5
5
  exports.SUPPORTED_DEPLOY_TARGETS = require_workspace.SUPPORTED_DEPLOY_TARGETS;
@@ -1,2 +1,2 @@
1
- import { D as PUBLIC_ENV_PREFIXES, O as getPublicEnvPrefix, S as validateWorkspaceConfig, _ as formatValidationErrors, a as getAppBuildOrder, b as isPhase2DeployTarget, c as getEndpointForStage, d as workspaceConstructGlobs, f as wrapSingleAppAsWorkspace, g as WorkspaceConfigSchema, h as SUPPORTED_DEPLOY_TARGETS, i as defineWorkspace, k as stripPublicPrefix, l as normalizeWorkspace, m as PHASE_2_DEPLOY_TARGETS, n as appConstructGlobs, o as getAppGkmConfig, p as isWorkspaceConfig, r as constructPatterns, s as getDependencyEnvVars, t as allConstructGlobs, u as processConfig, v as getDeployTargetError, x as safeValidateWorkspaceConfig, y as isDeployTargetSupported } from "../workspace-DI-M3BnN.mjs";
1
+ import { A as stripPublicPrefix, O as PUBLIC_ENV_PREFIXES, S as validateWorkspaceConfig, _ as formatValidationErrors, a as getAppBuildOrder, b as isPhase2DeployTarget, c as getEndpointForStage, d as workspaceConstructGlobs, f as wrapSingleAppAsWorkspace, g as WorkspaceConfigSchema, h as SUPPORTED_DEPLOY_TARGETS, i as defineWorkspace, k as getPublicEnvPrefix, l as normalizeWorkspace, m as PHASE_2_DEPLOY_TARGETS, n as appConstructGlobs, o as getAppGkmConfig, p as isWorkspaceConfig, r as constructPatterns, s as getDependencyEnvVars, t as allConstructGlobs, u as processConfig, v as getDeployTargetError, x as safeValidateWorkspaceConfig, y as isDeployTargetSupported } from "../workspace-Bu3xNCej.mjs";
2
2
  export { PHASE_2_DEPLOY_TARGETS, PUBLIC_ENV_PREFIXES, SUPPORTED_DEPLOY_TARGETS, WorkspaceConfigSchema, allConstructGlobs, appConstructGlobs, constructPatterns, defineWorkspace, formatValidationErrors, getAppBuildOrder, getAppGkmConfig, getDependencyEnvVars, getDeployTargetError, getEndpointForStage, getPublicEnvPrefix, isDeployTargetSupported, isPhase2DeployTarget, isWorkspaceConfig, normalizeWorkspace, processConfig, safeValidateWorkspaceConfig, stripPublicPrefix, validateWorkspaceConfig, workspaceConstructGlobs, wrapSingleAppAsWorkspace };
@@ -6558,6 +6558,6 @@ function getEndpointForStage(dokployConfig, stage) {
6558
6558
  return dokployConfig.endpoint;
6559
6559
  }
6560
6560
  //#endregion
6561
- export { InvalidStages as C, PUBLIC_ENV_PREFIXES as D, stageProblems as E, getPublicEnvPrefix as O, validateWorkspaceConfig as S, deployedProblems as T, formatValidationErrors as _, getAppBuildOrder as a, isPhase2DeployTarget as b, getEndpointForStage as c, workspaceConstructGlobs as d, wrapSingleAppAsWorkspace as f, WorkspaceConfigSchema as g, SUPPORTED_DEPLOY_TARGETS as h, defineWorkspace as i, stripPublicPrefix as k, normalizeWorkspace as l, PHASE_2_DEPLOY_TARGETS as m, appConstructGlobs as n, getAppGkmConfig as o, isWorkspaceConfig as p, constructPatterns as r, getDependencyEnvVars as s, allConstructGlobs as t, processConfig as u, getDeployTargetError as v, assertDeployedStage as w, safeValidateWorkspaceConfig as x, isDeployTargetSupported as y };
6561
+ export { stripPublicPrefix as A, InvalidStages as C, stageProblems as D, deployedProblems as E, PUBLIC_ENV_PREFIXES as O, validateWorkspaceConfig as S, assertDeployedStage as T, formatValidationErrors as _, getAppBuildOrder as a, isPhase2DeployTarget as b, getEndpointForStage as c, workspaceConstructGlobs as d, wrapSingleAppAsWorkspace as f, WorkspaceConfigSchema as g, SUPPORTED_DEPLOY_TARGETS as h, defineWorkspace as i, getPublicEnvPrefix as k, normalizeWorkspace as l, PHASE_2_DEPLOY_TARGETS as m, appConstructGlobs as n, getAppGkmConfig as o, isWorkspaceConfig as p, constructPatterns as r, getDependencyEnvVars as s, allConstructGlobs as t, processConfig as u, getDeployTargetError as v, TEST_STAGE as w, safeValidateWorkspaceConfig as x, isDeployTargetSupported as y };
6562
6562
 
6563
- //# sourceMappingURL=workspace-DI-M3BnN.mjs.map
6563
+ //# sourceMappingURL=workspace-Bu3xNCej.mjs.map