@kindgi/cli 0.0.0-bootstrap.0 → 0.1.0
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.
- package/LICENSE +201 -0
- package/README.md +690 -1
- package/dist/build/apt.d.ts +16 -0
- package/dist/build/apt.d.ts.map +1 -0
- package/dist/build/apt.js +50 -0
- package/dist/build/apt.js.map +1 -0
- package/dist/build/bundle.d.ts +45 -0
- package/dist/build/bundle.d.ts.map +1 -0
- package/dist/build/bundle.js +121 -0
- package/dist/build/bundle.js.map +1 -0
- package/dist/build/containerfile.d.ts +27 -0
- package/dist/build/containerfile.d.ts.map +1 -0
- package/dist/build/containerfile.js +196 -0
- package/dist/build/containerfile.js.map +1 -0
- package/dist/build/context-files.d.ts +24 -0
- package/dist/build/context-files.d.ts.map +1 -0
- package/dist/build/context-files.js +99 -0
- package/dist/build/context-files.js.map +1 -0
- package/dist/build/defaults.d.ts +66 -0
- package/dist/build/defaults.d.ts.map +1 -0
- package/dist/build/defaults.js +586 -0
- package/dist/build/defaults.js.map +1 -0
- package/dist/build/envelope.d.ts +72 -0
- package/dist/build/envelope.d.ts.map +1 -0
- package/dist/build/envelope.js +76 -0
- package/dist/build/envelope.js.map +1 -0
- package/dist/build/host-install.d.ts +48 -0
- package/dist/build/host-install.d.ts.map +1 -0
- package/dist/build/host-install.js +375 -0
- package/dist/build/host-install.js.map +1 -0
- package/dist/build/image-config.d.ts +32 -0
- package/dist/build/image-config.d.ts.map +1 -0
- package/dist/build/image-config.js +129 -0
- package/dist/build/image-config.js.map +1 -0
- package/dist/build/integrity.d.ts +51 -0
- package/dist/build/integrity.d.ts.map +1 -0
- package/dist/build/integrity.js +75 -0
- package/dist/build/integrity.js.map +1 -0
- package/dist/build/node-image.d.ts +18 -0
- package/dist/build/node-image.d.ts.map +1 -0
- package/dist/build/node-image.js +46 -0
- package/dist/build/node-image.js.map +1 -0
- package/dist/build/pack-root.d.ts +69 -0
- package/dist/build/pack-root.d.ts.map +1 -0
- package/dist/build/pack-root.js +102 -0
- package/dist/build/pack-root.js.map +1 -0
- package/dist/build/poetry-requirements.d.ts +5 -0
- package/dist/build/poetry-requirements.d.ts.map +1 -0
- package/dist/build/poetry-requirements.js +8 -0
- package/dist/build/poetry-requirements.js.map +1 -0
- package/dist/build/python-image.d.ts +40 -0
- package/dist/build/python-image.d.ts.map +1 -0
- package/dist/build/python-image.js +197 -0
- package/dist/build/python-image.js.map +1 -0
- package/dist/build/runners.d.ts +259 -0
- package/dist/build/runners.d.ts.map +1 -0
- package/dist/build/runners.js +4 -0
- package/dist/build/runners.js.map +1 -0
- package/dist/build/version-specifier.d.ts +2 -0
- package/dist/build/version-specifier.d.ts.map +1 -0
- package/dist/build/version-specifier.js +70 -0
- package/dist/build/version-specifier.js.map +1 -0
- package/dist/cli-package.d.ts +15 -0
- package/dist/cli-package.d.ts.map +1 -0
- package/dist/cli-package.js +37 -0
- package/dist/cli-package.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +26 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/adapters.d.ts +3 -0
- package/dist/commands/adapters.d.ts.map +1 -0
- package/dist/commands/adapters.js +148 -0
- package/dist/commands/adapters.js.map +1 -0
- package/dist/commands/agents.d.ts +3 -0
- package/dist/commands/agents.d.ts.map +1 -0
- package/dist/commands/agents.js +82 -0
- package/dist/commands/agents.js.map +1 -0
- package/dist/commands/approvals.d.ts +3 -0
- package/dist/commands/approvals.d.ts.map +1 -0
- package/dist/commands/approvals.js +92 -0
- package/dist/commands/approvals.js.map +1 -0
- package/dist/commands/artifacts.d.ts +3 -0
- package/dist/commands/artifacts.d.ts.map +1 -0
- package/dist/commands/artifacts.js +87 -0
- package/dist/commands/artifacts.js.map +1 -0
- package/dist/commands/auth.d.ts +3 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +88 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/build.d.ts +39 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +872 -0
- package/dist/commands/build.js.map +1 -0
- package/dist/commands/capabilities.d.ts +3 -0
- package/dist/commands/capabilities.d.ts.map +1 -0
- package/dist/commands/capabilities.js +38 -0
- package/dist/commands/capabilities.js.map +1 -0
- package/dist/commands/conversations.d.ts +3 -0
- package/dist/commands/conversations.d.ts.map +1 -0
- package/dist/commands/conversations.js +75 -0
- package/dist/commands/conversations.js.map +1 -0
- package/dist/commands/deploy.d.ts +7 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +697 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +44 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +1032 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/env.d.ts +27 -0
- package/dist/commands/env.d.ts.map +1 -0
- package/dist/commands/env.js +816 -0
- package/dist/commands/env.js.map +1 -0
- package/dist/commands/feedback.d.ts +33 -0
- package/dist/commands/feedback.d.ts.map +1 -0
- package/dist/commands/feedback.js +374 -0
- package/dist/commands/feedback.js.map +1 -0
- package/dist/commands/flows.d.ts +3 -0
- package/dist/commands/flows.d.ts.map +1 -0
- package/dist/commands/flows.js +60 -0
- package/dist/commands/flows.js.map +1 -0
- package/dist/commands/guardrails.d.ts +3 -0
- package/dist/commands/guardrails.d.ts.map +1 -0
- package/dist/commands/guardrails.js +89 -0
- package/dist/commands/guardrails.js.map +1 -0
- package/dist/commands/health.d.ts +8 -0
- package/dist/commands/health.d.ts.map +1 -0
- package/dist/commands/health.js +40 -0
- package/dist/commands/health.js.map +1 -0
- package/dist/commands/helpers.d.ts +37 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +93 -0
- package/dist/commands/helpers.js.map +1 -0
- package/dist/commands/index.d.ts +7 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +90 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init.d.ts +46 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +455 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/key.d.ts +3 -0
- package/dist/commands/key.d.ts.map +1 -0
- package/dist/commands/key.js +322 -0
- package/dist/commands/key.js.map +1 -0
- package/dist/commands/mcp.d.ts +43 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +433 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/memory.d.ts +3 -0
- package/dist/commands/memory.d.ts.map +1 -0
- package/dist/commands/memory.js +82 -0
- package/dist/commands/memory.js.map +1 -0
- package/dist/commands/observations.d.ts +3 -0
- package/dist/commands/observations.d.ts.map +1 -0
- package/dist/commands/observations.js +27 -0
- package/dist/commands/observations.js.map +1 -0
- package/dist/commands/proposals.d.ts +3 -0
- package/dist/commands/proposals.d.ts.map +1 -0
- package/dist/commands/proposals.js +116 -0
- package/dist/commands/proposals.js.map +1 -0
- package/dist/commands/provenance.d.ts +3 -0
- package/dist/commands/provenance.d.ts.map +1 -0
- package/dist/commands/provenance.js +53 -0
- package/dist/commands/provenance.js.map +1 -0
- package/dist/commands/providers.d.ts +3 -0
- package/dist/commands/providers.d.ts.map +1 -0
- package/dist/commands/providers.js +202 -0
- package/dist/commands/providers.js.map +1 -0
- package/dist/commands/reviewers.d.ts +3 -0
- package/dist/commands/reviewers.d.ts.map +1 -0
- package/dist/commands/reviewers.js +78 -0
- package/dist/commands/reviewers.js.map +1 -0
- package/dist/commands/runs.d.ts +3 -0
- package/dist/commands/runs.d.ts.map +1 -0
- package/dist/commands/runs.js +221 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/secrets.d.ts +21 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +750 -0
- package/dist/commands/secrets.js.map +1 -0
- package/dist/commands/skills.d.ts +64 -0
- package/dist/commands/skills.d.ts.map +1 -0
- package/dist/commands/skills.js +430 -0
- package/dist/commands/skills.js.map +1 -0
- package/dist/commands/test.d.ts +5 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +181 -0
- package/dist/commands/test.js.map +1 -0
- package/dist/commands/tokens.d.ts +3 -0
- package/dist/commands/tokens.d.ts.map +1 -0
- package/dist/commands/tokens.js +33 -0
- package/dist/commands/tokens.js.map +1 -0
- package/dist/commands/tools.d.ts +3 -0
- package/dist/commands/tools.d.ts.map +1 -0
- package/dist/commands/tools.js +153 -0
- package/dist/commands/tools.js.map +1 -0
- package/dist/commands/types.d.ts +54 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +4 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/unwired.d.ts +17 -0
- package/dist/commands/unwired.d.ts.map +1 -0
- package/dist/commands/unwired.js +41 -0
- package/dist/commands/unwired.js.map +1 -0
- package/dist/commands/version.d.ts +8 -0
- package/dist/commands/version.d.ts.map +1 -0
- package/dist/commands/version.js +41 -0
- package/dist/commands/version.js.map +1 -0
- package/dist/config.d.ts +42 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +83 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +153 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +44 -0
- package/dist/context.js.map +1 -0
- package/dist/deploy/defaults.d.ts +14 -0
- package/dist/deploy/defaults.d.ts.map +1 -0
- package/dist/deploy/defaults.js +37 -0
- package/dist/deploy/defaults.js.map +1 -0
- package/dist/deploy/envelope-loader.d.ts +24 -0
- package/dist/deploy/envelope-loader.d.ts.map +1 -0
- package/dist/deploy/envelope-loader.js +141 -0
- package/dist/deploy/envelope-loader.js.map +1 -0
- package/dist/deploy/post.d.ts +48 -0
- package/dist/deploy/post.d.ts.map +1 -0
- package/dist/deploy/post.js +210 -0
- package/dist/deploy/post.js.map +1 -0
- package/dist/deploy/response.d.ts +42 -0
- package/dist/deploy/response.d.ts.map +1 -0
- package/dist/deploy/response.js +128 -0
- package/dist/deploy/response.js.map +1 -0
- package/dist/deploy/runners.d.ts +218 -0
- package/dist/deploy/runners.d.ts.map +1 -0
- package/dist/deploy/runners.js +4 -0
- package/dist/deploy/runners.js.map +1 -0
- package/dist/dev/bundler.d.ts +8 -0
- package/dist/dev/bundler.d.ts.map +1 -0
- package/dist/dev/bundler.js +154 -0
- package/dist/dev/bundler.js.map +1 -0
- package/dist/dev/defaults.d.ts +138 -0
- package/dist/dev/defaults.d.ts.map +1 -0
- package/dist/dev/defaults.js +691 -0
- package/dist/dev/defaults.js.map +1 -0
- package/dist/dev/docker-compose.dev.yml +56 -0
- package/dist/dev/index-child.d.ts +2 -0
- package/dist/dev/index-child.d.ts.map +1 -0
- package/dist/dev/index-child.js +53 -0
- package/dist/dev/index-child.js.map +1 -0
- package/dist/dev/pack-code.d.ts +36 -0
- package/dist/dev/pack-code.d.ts.map +1 -0
- package/dist/dev/pack-code.js +129 -0
- package/dist/dev/pack-code.js.map +1 -0
- package/dist/dev/pack-env.d.ts +13 -0
- package/dist/dev/pack-env.d.ts.map +1 -0
- package/dist/dev/pack-env.js +36 -0
- package/dist/dev/pack-env.js.map +1 -0
- package/dist/dev/pack-service.d.ts +32 -0
- package/dist/dev/pack-service.d.ts.map +1 -0
- package/dist/dev/pack-service.js +188 -0
- package/dist/dev/pack-service.js.map +1 -0
- package/dist/dev/paths.d.ts +22 -0
- package/dist/dev/paths.d.ts.map +1 -0
- package/dist/dev/paths.js +35 -0
- package/dist/dev/paths.js.map +1 -0
- package/dist/dev/python-builder.d.ts +16 -0
- package/dist/dev/python-builder.d.ts.map +1 -0
- package/dist/dev/python-builder.js +122 -0
- package/dist/dev/python-builder.js.map +1 -0
- package/dist/dev/register.d.ts +71 -0
- package/dist/dev/register.d.ts.map +1 -0
- package/dist/dev/register.js +54 -0
- package/dist/dev/register.js.map +1 -0
- package/dist/dev/runners.d.ts +287 -0
- package/dist/dev/runners.d.ts.map +1 -0
- package/dist/dev/runners.js +4 -0
- package/dist/dev/runners.js.map +1 -0
- package/dist/dev/runtime-container.d.ts +62 -0
- package/dist/dev/runtime-container.d.ts.map +1 -0
- package/dist/dev/runtime-container.js +164 -0
- package/dist/dev/runtime-container.js.map +1 -0
- package/dist/dev/runtime-env.d.ts +62 -0
- package/dist/dev/runtime-env.d.ts.map +1 -0
- package/dist/dev/runtime-env.js +100 -0
- package/dist/dev/runtime-env.js.map +1 -0
- package/dist/dev/runtime-image.d.ts +13 -0
- package/dist/dev/runtime-image.d.ts.map +1 -0
- package/dist/dev/runtime-image.js +15 -0
- package/dist/dev/runtime-image.js.map +1 -0
- package/dist/env/defaults.d.ts +3 -0
- package/dist/env/defaults.d.ts.map +1 -0
- package/dist/env/defaults.js +22 -0
- package/dist/env/defaults.js.map +1 -0
- package/dist/env/pack-env-plan.d.ts +70 -0
- package/dist/env/pack-env-plan.d.ts.map +1 -0
- package/dist/env/pack-env-plan.js +236 -0
- package/dist/env/pack-env-plan.js.map +1 -0
- package/dist/env/parser.d.ts +9 -0
- package/dist/env/parser.d.ts.map +1 -0
- package/dist/env/parser.js +11 -0
- package/dist/env/parser.js.map +1 -0
- package/dist/env/project-env.d.ts +34 -0
- package/dist/env/project-env.d.ts.map +1 -0
- package/dist/env/project-env.js +45 -0
- package/dist/env/project-env.js.map +1 -0
- package/dist/env/runners.d.ts +22 -0
- package/dist/env/runners.d.ts.map +1 -0
- package/dist/env/runners.js +4 -0
- package/dist/env/runners.js.map +1 -0
- package/dist/env/writer.d.ts +7 -0
- package/dist/env/writer.d.ts.map +1 -0
- package/dist/env/writer.js +9 -0
- package/dist/env/writer.js.map +1 -0
- package/dist/errors.d.ts +31 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +107 -0
- package/dist/errors.js.map +1 -0
- package/dist/help.d.ts +5 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +61 -0
- package/dist/help.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/init/augment-scaffolder.d.ts +74 -0
- package/dist/init/augment-scaffolder.d.ts.map +1 -0
- package/dist/init/augment-scaffolder.js +459 -0
- package/dist/init/augment-scaffolder.js.map +1 -0
- package/dist/init/dependency-specs.d.ts +58 -0
- package/dist/init/dependency-specs.d.ts.map +1 -0
- package/dist/init/dependency-specs.js +153 -0
- package/dist/init/dependency-specs.js.map +1 -0
- package/dist/init/gitignore-patcher.d.ts +23 -0
- package/dist/init/gitignore-patcher.d.ts.map +1 -0
- package/dist/init/gitignore-patcher.js +103 -0
- package/dist/init/gitignore-patcher.js.map +1 -0
- package/dist/init/mode-detect.d.ts +34 -0
- package/dist/init/mode-detect.d.ts.map +1 -0
- package/dist/init/mode-detect.js +115 -0
- package/dist/init/mode-detect.js.map +1 -0
- package/dist/init/package-json-patcher.d.ts +36 -0
- package/dist/init/package-json-patcher.d.ts.map +1 -0
- package/dist/init/package-json-patcher.js +128 -0
- package/dist/init/package-json-patcher.js.map +1 -0
- package/dist/init/pyproject-patcher.d.ts +56 -0
- package/dist/init/pyproject-patcher.d.ts.map +1 -0
- package/dist/init/pyproject-patcher.js +324 -0
- package/dist/init/pyproject-patcher.js.map +1 -0
- package/dist/init/python-augment.d.ts +22 -0
- package/dist/init/python-augment.d.ts.map +1 -0
- package/dist/init/python-augment.js +300 -0
- package/dist/init/python-augment.js.map +1 -0
- package/dist/init/template-files.d.ts +16 -0
- package/dist/init/template-files.d.ts.map +1 -0
- package/dist/init/template-files.js +38 -0
- package/dist/init/template-files.js.map +1 -0
- package/dist/key/defaults.d.ts +3 -0
- package/dist/key/defaults.d.ts.map +1 -0
- package/dist/key/defaults.js +60 -0
- package/dist/key/defaults.js.map +1 -0
- package/dist/key/fingerprint.d.ts +6 -0
- package/dist/key/fingerprint.d.ts.map +1 -0
- package/dist/key/fingerprint.js +25 -0
- package/dist/key/fingerprint.js.map +1 -0
- package/dist/key/paths.d.ts +30 -0
- package/dist/key/paths.d.ts.map +1 -0
- package/dist/key/paths.js +51 -0
- package/dist/key/paths.js.map +1 -0
- package/dist/key/runners.d.ts +60 -0
- package/dist/key/runners.d.ts.map +1 -0
- package/dist/key/runners.js +4 -0
- package/dist/key/runners.js.map +1 -0
- package/dist/main.d.ts +106 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +198 -0
- package/dist/main.js.map +1 -0
- package/dist/mcp/launcher.d.ts +143 -0
- package/dist/mcp/launcher.d.ts.map +1 -0
- package/dist/mcp/launcher.js +393 -0
- package/dist/mcp/launcher.js.map +1 -0
- package/dist/mcp/preset-loader.d.ts +29 -0
- package/dist/mcp/preset-loader.d.ts.map +1 -0
- package/dist/mcp/preset-loader.js +217 -0
- package/dist/mcp/preset-loader.js.map +1 -0
- package/dist/mcp/preset-types.d.ts +71 -0
- package/dist/mcp/preset-types.d.ts.map +1 -0
- package/dist/mcp/preset-types.js +4 -0
- package/dist/mcp/preset-types.js.map +1 -0
- package/dist/mcp/presets/README.md +68 -0
- package/dist/mcp/presets/postgres.json +15 -0
- package/dist/output.d.ts +26 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +46 -0
- package/dist/output.js.map +1 -0
- package/dist/pack-config.d.ts +37 -0
- package/dist/pack-config.d.ts.map +1 -0
- package/dist/pack-config.js +65 -0
- package/dist/pack-config.js.map +1 -0
- package/dist/package-manager.d.ts +33 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +129 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/parse.d.ts +82 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +99 -0
- package/dist/parse.js.map +1 -0
- package/dist/providers/preset-loader.d.ts +47 -0
- package/dist/providers/preset-loader.d.ts.map +1 -0
- package/dist/providers/preset-loader.js +107 -0
- package/dist/providers/preset-loader.js.map +1 -0
- package/dist/providers/presets/anthropic.json +47 -0
- package/dist/providers/presets/gemini.json +40 -0
- package/dist/reference.d.ts +28 -0
- package/dist/reference.d.ts.map +1 -0
- package/dist/reference.js +52 -0
- package/dist/reference.js.map +1 -0
- package/dist/sdk-package.d.ts +9 -0
- package/dist/sdk-package.d.ts.map +1 -0
- package/dist/sdk-package.js +20 -0
- package/dist/sdk-package.js.map +1 -0
- package/dist/sdk-skills/kindgi-authoring-agents/SKILL.md +253 -0
- package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +299 -0
- package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +291 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +289 -0
- package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +704 -0
- package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +288 -0
- package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +211 -0
- package/dist/sdk-skills/kindgi-getting-started/SKILL.md +181 -0
- package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +205 -0
- package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +322 -0
- package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +173 -0
- package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +299 -0
- package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +240 -0
- package/dist/stop-signal.d.ts +15 -0
- package/dist/stop-signal.d.ts.map +1 -0
- package/dist/stop-signal.js +16 -0
- package/dist/stop-signal.js.map +1 -0
- package/dist/templates/minimal/.gitignore +8 -0
- package/dist/templates/minimal/.nvmrc +1 -0
- package/dist/templates/minimal/AGENTS.md +23 -0
- package/dist/templates/minimal/README.md.tmpl +69 -0
- package/dist/templates/minimal/agents/.gitkeep +0 -0
- package/dist/templates/minimal/flows/.gitkeep +0 -0
- package/dist/templates/minimal/guardrails/.gitkeep +0 -0
- package/dist/templates/minimal/kindgi.config.ts.tmpl +44 -0
- package/dist/templates/minimal/package.json.tmpl +25 -0
- package/dist/templates/minimal/pnpm-workspace.yaml +6 -0
- package/dist/templates/minimal/tools/.gitkeep +0 -0
- package/dist/templates/minimal/tsconfig.json.tmpl +27 -0
- package/dist/templates/minimal/vitest.config.ts.tmpl +14 -0
- package/dist/templates/python/.gitignore +9 -0
- package/dist/templates/python/AGENTS.md +30 -0
- package/dist/templates/python/README.md.tmpl +56 -0
- package/dist/templates/python/agents/echo_agent.py.tmpl +23 -0
- package/dist/templates/python/flows/echo_flow.py.tmpl +17 -0
- package/dist/templates/python/guardrails/response_not_empty.py.tmpl +27 -0
- package/dist/templates/python/pyproject.toml.tmpl +42 -0
- package/dist/templates/python/tests/test_tools.py.tmpl +22 -0
- package/dist/templates/python/tools/echo.py.tmpl +27 -0
- package/dist/templates/python/tools/greet.py.tmpl +20 -0
- package/dist/templates/sample/.gitignore +8 -0
- package/dist/templates/sample/.nvmrc +1 -0
- package/dist/templates/sample/AGENTS.md +23 -0
- package/dist/templates/sample/README.md.tmpl +85 -0
- package/dist/templates/sample/agents/echo-agent/index.ts.tmpl +34 -0
- package/dist/templates/sample/flows/echo-flow/index.ts.tmpl +59 -0
- package/dist/templates/sample/guardrails/response-not-empty/index.ts.tmpl +50 -0
- package/dist/templates/sample/kindgi.config.ts.tmpl +41 -0
- package/dist/templates/sample/package.json.tmpl +25 -0
- package/dist/templates/sample/pnpm-workspace.yaml +6 -0
- package/dist/templates/sample/tools/echo/index.test.ts.tmpl +31 -0
- package/dist/templates/sample/tools/echo/index.ts.tmpl +37 -0
- package/dist/templates/sample/tools/fetch-httpbin/index.ts.tmpl +60 -0
- package/dist/templates/sample/tools/greet/index.ts.tmpl +35 -0
- package/dist/templates/sample/tsconfig.json.tmpl +27 -0
- package/dist/templates/sample/vitest.config.ts.tmpl +14 -0
- package/dist/test/defaults.d.ts +3 -0
- package/dist/test/defaults.d.ts.map +1 -0
- package/dist/test/defaults.js +104 -0
- package/dist/test/defaults.js.map +1 -0
- package/dist/test/runners.d.ts +60 -0
- package/dist/test/runners.d.ts.map +1 -0
- package/dist/test/runners.js +4 -0
- package/dist/test/runners.js.map +1 -0
- package/dist/version-info.d.ts +9 -0
- package/dist/version-info.d.ts.map +1 -0
- package/dist/version-info.js +27 -0
- package/dist/version-info.js.map +1 -0
- package/dist/write-fully.d.ts +13 -0
- package/dist/write-fully.d.ts.map +1 -0
- package/dist/write-fully.js +17 -0
- package/dist/write-fully.js.map +1 -0
- package/package.json +63 -4
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
/**
|
|
4
|
+
* `kindgi env` — per-environment value management.
|
|
5
|
+
*
|
|
6
|
+
* Local-first: `list` / `set` / `unset` operate on `.env.<envName>`
|
|
7
|
+
* files sitting next to `kindgi.config.ts`, which is exactly the
|
|
8
|
+
* shape `kindgi deploy`'s secrets-sync warning path detects today.
|
|
9
|
+
* `pull` bridges to the API's dedicated `/v1/env/*` surface.
|
|
10
|
+
*
|
|
11
|
+
* Subcommands:
|
|
12
|
+
* - `list [--env <name>] [--reveal] [--force-reveal] [--path <dir>]`
|
|
13
|
+
* - `set <KEY> <VALUE> [--env <name>] [--force] [--path <dir>]`
|
|
14
|
+
* - `unset <KEY> [--env <name>] [--path <dir>]`
|
|
15
|
+
* - `pull [--env <name>] --scope=<kind>[:id] [--path <dir>] [--overwrite-existing]`
|
|
16
|
+
*
|
|
17
|
+
* Every filesystem side-effect flows through `EnvRunners` so tests
|
|
18
|
+
* substitute stubs. `kindgi.config.ts` loading reuses the same
|
|
19
|
+
* `buildConfigLoader` seam `kindgi build` + `kindgi deploy`
|
|
20
|
+
* already thread — one config loader, one test-injection point.
|
|
21
|
+
*/
|
|
22
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
23
|
+
import * as readline from 'node:readline';
|
|
24
|
+
import { readEnvLayers } from '@kindgi/dotenv-file';
|
|
25
|
+
import { envVarsForTarget, renderEnvExample } from '@kindgi/env-schema';
|
|
26
|
+
import { displayEnvPath, isRuntimeKey, resolvePackEnvFiles } from '@kindgi/secrets-dotenv';
|
|
27
|
+
import { describePackEnvPlan, planPackEnv, renderGcloud, renderTerraform, } from '../env/pack-env-plan.js';
|
|
28
|
+
import { ENV_KEY_REGEX } from '../env/parser.js';
|
|
29
|
+
import { describeEnvDiagnostics, loadLocalEnvSettings } from '../env/project-env.js';
|
|
30
|
+
import { EnvValueNotRepresentableError, setKey, unsetKey } from '../env/writer.js';
|
|
31
|
+
import { renderJson } from '../output.js';
|
|
32
|
+
import { commandResultFromThrown, stringFlag } from './helpers.js';
|
|
33
|
+
// ---------------------------------------------------------------------
|
|
34
|
+
// Guardrails
|
|
35
|
+
// ---------------------------------------------------------------------
|
|
36
|
+
/**
|
|
37
|
+
* Keys the CLI refuses to write / unset via `kindgi env`: framework
|
|
38
|
+
* runtime config, which is always `KINDGI_*`-prefixed (see
|
|
39
|
+
* `@kindgi/env-schema`). Every un-prefixed name — `DATABASE_URL`
|
|
40
|
+
* included — belongs to the pack's agents or the host application.
|
|
41
|
+
* `kindgi env` is per-pack per-env; framework globals live in the
|
|
42
|
+
* shell's own env / secret manager.
|
|
43
|
+
*/
|
|
44
|
+
export const REFUSED_KEY_PREFIXES = ['KINDGI_'];
|
|
45
|
+
export function isRefusedKey(key) {
|
|
46
|
+
for (const prefix of REFUSED_KEY_PREFIXES) {
|
|
47
|
+
if (key.startsWith(prefix))
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The env files for `--env` (default `staging`). `local` is the
|
|
54
|
+
* project's own files (`.env`, `.env.local`, or `dev.envFiles`); any
|
|
55
|
+
* other environment is `.env.<envName>`.
|
|
56
|
+
*/
|
|
57
|
+
async function resolvePaths(ctx) {
|
|
58
|
+
const pathFlag = ctx.options.path;
|
|
59
|
+
const rawPath = typeof pathFlag === 'string' && pathFlag !== '' ? pathFlag : ctx.cwd;
|
|
60
|
+
const packDir = isAbsolute(rawPath) ? rawPath : resolve(ctx.cwd, rawPath);
|
|
61
|
+
const envFlag = ctx.options.env;
|
|
62
|
+
const envName = typeof envFlag === 'string' && envFlag !== '' ? envFlag : 'staging';
|
|
63
|
+
const settings = await loadLocalEnvSettings(ctx, packDir);
|
|
64
|
+
if (settings.kind === 'error') {
|
|
65
|
+
return { kind: 'error', stderr: `kindgi env: ${settings.message}\n`, exitCode: 1 };
|
|
66
|
+
}
|
|
67
|
+
const files = resolvePackEnvFiles({
|
|
68
|
+
packDir,
|
|
69
|
+
envName,
|
|
70
|
+
...(settings.localEnvFiles !== undefined && { localEnvFiles: settings.localEnvFiles }),
|
|
71
|
+
});
|
|
72
|
+
return {
|
|
73
|
+
kind: 'ok',
|
|
74
|
+
paths: {
|
|
75
|
+
packDir,
|
|
76
|
+
envName,
|
|
77
|
+
envFilePath: files.write,
|
|
78
|
+
readPaths: files.read,
|
|
79
|
+
config: settings.config,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** Read every file for the environment as one merged, expanded view. */
|
|
84
|
+
async function readFileView(ctx, runners, paths) {
|
|
85
|
+
const layers = await Promise.all(paths.readPaths.map(async (source) => ({ source, contents: await runners.readFile(source) })));
|
|
86
|
+
return readEnvLayers(layers, { env: ctx.env });
|
|
87
|
+
}
|
|
88
|
+
const shown = (paths, file) => displayEnvPath(paths.packDir, file);
|
|
89
|
+
function pickRunners(ctx) {
|
|
90
|
+
if (ctx.envRunners !== undefined)
|
|
91
|
+
return { kind: 'ok', runners: ctx.envRunners };
|
|
92
|
+
return {
|
|
93
|
+
kind: 'error',
|
|
94
|
+
stderr: 'Internal error: kindgi env requires env runners to be wired. ' +
|
|
95
|
+
'Rebuild the CLI (`pnpm --filter @kindgi/cli build`).\n',
|
|
96
|
+
exitCode: 1,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* `environments.<envName>.env` from `kindgi.config.ts`. A missing or
|
|
101
|
+
* unloadable config contributes nothing — `kindgi env list` still
|
|
102
|
+
* shows the env files.
|
|
103
|
+
*/
|
|
104
|
+
function configEnvBlock(config, envName) {
|
|
105
|
+
if (config === undefined)
|
|
106
|
+
return {};
|
|
107
|
+
const envs = (config.environments ?? {});
|
|
108
|
+
const block = envs[envName];
|
|
109
|
+
if (block === undefined || typeof block !== 'object')
|
|
110
|
+
return {};
|
|
111
|
+
const env = block.env;
|
|
112
|
+
if (env === undefined || typeof env !== 'object')
|
|
113
|
+
return {};
|
|
114
|
+
const out = {};
|
|
115
|
+
for (const [k, v] of Object.entries(env)) {
|
|
116
|
+
if (typeof v === 'string')
|
|
117
|
+
out[k] = v;
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
// ---------------------------------------------------------------------
|
|
122
|
+
// `kindgi env list`
|
|
123
|
+
// ---------------------------------------------------------------------
|
|
124
|
+
const listCmd = {
|
|
125
|
+
kind: 'leaf',
|
|
126
|
+
name: 'list',
|
|
127
|
+
description: 'List resolved env values for the target env (merges config + .env.<envName>).',
|
|
128
|
+
usage: 'kindgi env list [--env <name>] [--reveal] [--force-reveal] [--path <dir>]',
|
|
129
|
+
optionSpec: {
|
|
130
|
+
env: {
|
|
131
|
+
type: 'string',
|
|
132
|
+
description: "The environment: `local` is the project's own env files, any other name its `.env.<name>`. Default: `staging`.",
|
|
133
|
+
},
|
|
134
|
+
reveal: {
|
|
135
|
+
type: 'boolean',
|
|
136
|
+
description: "Print the values instead of redacting them. Refused when stdout isn't a terminal (a file, a pipe), unless `--force-reveal` is also given.",
|
|
137
|
+
},
|
|
138
|
+
'force-reveal': {
|
|
139
|
+
type: 'boolean',
|
|
140
|
+
description: "With `--reveal`, print the values even when stdout isn't a terminal (a file, a pipe, CI).",
|
|
141
|
+
},
|
|
142
|
+
path: {
|
|
143
|
+
type: 'string',
|
|
144
|
+
description: 'The pack root, where the env files are. Default: the current directory.',
|
|
145
|
+
},
|
|
146
|
+
},
|
|
147
|
+
run: async (ctx) => {
|
|
148
|
+
const runnersOutcome = pickRunners(ctx);
|
|
149
|
+
if (runnersOutcome.kind === 'error')
|
|
150
|
+
return runnersOutcome;
|
|
151
|
+
const runners = runnersOutcome.runners;
|
|
152
|
+
const resolved = await resolvePaths(ctx);
|
|
153
|
+
if (resolved.kind === 'error')
|
|
154
|
+
return resolved;
|
|
155
|
+
const paths = resolved.paths;
|
|
156
|
+
const reveal = ctx.options.reveal === true;
|
|
157
|
+
const forceReveal = ctx.options['force-reveal'] === true;
|
|
158
|
+
// Reveal gate: the values go to stdout, so stdout must be a terminal
|
|
159
|
+
// (not a file or a pipe) unless --force-reveal says otherwise.
|
|
160
|
+
const stdoutIsTty = typeof process.stdout.isTTY === 'boolean' ? process.stdout.isTTY : false;
|
|
161
|
+
if (reveal && !stdoutIsTty && !forceReveal) {
|
|
162
|
+
return {
|
|
163
|
+
kind: 'error',
|
|
164
|
+
stderr: 'kindgi env list --reveal refuses to emit values to a non-TTY without ' +
|
|
165
|
+
'--force-reveal. Piping raw secrets to a log file / CI capture is almost ' +
|
|
166
|
+
'always accidental.\n',
|
|
167
|
+
exitCode: 1,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const view = await readFileView(ctx, runners, paths);
|
|
171
|
+
const rows = listRows(paths, view, configEnvBlock(paths.config, paths.envName), reveal);
|
|
172
|
+
const warnings = describeEnvDiagnostics(paths.packDir, view.diagnostics);
|
|
173
|
+
const summary = {
|
|
174
|
+
envName: paths.envName,
|
|
175
|
+
packDir: paths.packDir,
|
|
176
|
+
envFilePath: paths.envFilePath,
|
|
177
|
+
envFiles: paths.readPaths.map((p) => ({
|
|
178
|
+
file: shown(paths, p),
|
|
179
|
+
exists: view.present.includes(p),
|
|
180
|
+
})),
|
|
181
|
+
reveal,
|
|
182
|
+
count: rows.length,
|
|
183
|
+
values: rows,
|
|
184
|
+
...(warnings.length > 0 && { warnings }),
|
|
185
|
+
};
|
|
186
|
+
const bannerLines = listBanner(paths.envName, summary.envFiles, reveal, rows, warnings);
|
|
187
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
188
|
+
return {
|
|
189
|
+
kind: 'ok',
|
|
190
|
+
rendered: { stdout: rendered.stdout, stderr: `${bannerLines.join('\n')}\n` },
|
|
191
|
+
};
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* One row per key. The env files override `environments.<name>.env`
|
|
196
|
+
* entries — the same shape `kindgi deploy`'s secrets-sync path uses.
|
|
197
|
+
*/
|
|
198
|
+
function listRows(paths, view, configBlock, reveal) {
|
|
199
|
+
const keys = new Set([...Object.keys(configBlock), ...Object.keys(view.values)]);
|
|
200
|
+
return [...keys].sort().map((key) => {
|
|
201
|
+
const inFile = Object.hasOwn(view.values, key);
|
|
202
|
+
const rawValue = (inFile ? view.values[key] : configBlock[key]) ?? '';
|
|
203
|
+
return {
|
|
204
|
+
key,
|
|
205
|
+
value: reveal ? rawValue : redact(rawValue),
|
|
206
|
+
source: inFile ? 'env-file' : 'config',
|
|
207
|
+
...(inFile && { file: shown(paths, view.origin[key] ?? '') }),
|
|
208
|
+
runtime: isRuntimeKey(key),
|
|
209
|
+
inFile,
|
|
210
|
+
inConfig: Object.hasOwn(configBlock, key),
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
function listBanner(envName, envFiles, reveal, rows, warnings) {
|
|
215
|
+
const lines = ['', ` Environment: ${envName}`];
|
|
216
|
+
for (const f of envFiles) {
|
|
217
|
+
lines.push(` Env file: ${f.file}${f.exists ? '' : ' (does not exist)'}`);
|
|
218
|
+
}
|
|
219
|
+
lines.push(` Reveal: ${reveal ? 'YES (values shown)' : 'NO (redacted; --reveal to show)'}`);
|
|
220
|
+
lines.push('');
|
|
221
|
+
lines.push(rows.length === 0 ? ' (no keys resolved)' : ` ${rows.length} key(s):`);
|
|
222
|
+
for (const row of rows) {
|
|
223
|
+
const where = row.file ?? row.source;
|
|
224
|
+
const tag = row.runtime ? `${where}, Kindgi runtime` : where;
|
|
225
|
+
lines.push(` ${row.key.padEnd(24, ' ')} ${row.value} [${tag}]`);
|
|
226
|
+
}
|
|
227
|
+
if (warnings.length > 0) {
|
|
228
|
+
lines.push('', ` ⚠ ${warnings.length} warning(s):`, ...warnings.map((w) => ` ${w}`));
|
|
229
|
+
}
|
|
230
|
+
lines.push('');
|
|
231
|
+
return lines;
|
|
232
|
+
}
|
|
233
|
+
// ---------------------------------------------------------------------
|
|
234
|
+
// `kindgi env set <KEY> <VALUE>`
|
|
235
|
+
// ---------------------------------------------------------------------
|
|
236
|
+
const setCmd = {
|
|
237
|
+
kind: 'leaf',
|
|
238
|
+
name: 'set',
|
|
239
|
+
description: 'Set a KEY=VALUE in .env.<envName>. Refuses to overwrite unless --force.',
|
|
240
|
+
usage: 'kindgi env set <KEY> <VALUE> [--env <name>] [--force] [--path <dir>]',
|
|
241
|
+
optionSpec: {
|
|
242
|
+
env: {
|
|
243
|
+
type: 'string',
|
|
244
|
+
description: "The environment: `local` is the project's own env files, any other name its `.env.<name>`. Default: `staging`.",
|
|
245
|
+
},
|
|
246
|
+
force: {
|
|
247
|
+
type: 'boolean',
|
|
248
|
+
description: 'Change a key an env file already sets: overwrite it, or override a lower-precedence file. By default `set` refuses.',
|
|
249
|
+
},
|
|
250
|
+
path: {
|
|
251
|
+
type: 'string',
|
|
252
|
+
description: 'The pack root, where the env files are. Default: the current directory.',
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
run: async (ctx) => {
|
|
256
|
+
const key = ctx.positionals[0];
|
|
257
|
+
const value = ctx.positionals[1];
|
|
258
|
+
if (typeof key !== 'string' || key === '') {
|
|
259
|
+
return {
|
|
260
|
+
kind: 'error',
|
|
261
|
+
stderr: 'Missing required argument: <KEY>.\n' +
|
|
262
|
+
'Usage: kindgi env set <KEY> <VALUE> [--env <name>] [--force]\n',
|
|
263
|
+
exitCode: 1,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
if (typeof value !== 'string') {
|
|
267
|
+
return {
|
|
268
|
+
kind: 'error',
|
|
269
|
+
stderr: 'Missing required argument: <VALUE>.\n' +
|
|
270
|
+
'Usage: kindgi env set <KEY> <VALUE> [--env <name>] [--force]\n',
|
|
271
|
+
exitCode: 1,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
if (!ENV_KEY_REGEX.test(key)) {
|
|
275
|
+
return {
|
|
276
|
+
kind: 'error',
|
|
277
|
+
stderr: `Invalid KEY: "${key}". Must match ${ENV_KEY_REGEX.source} — POSIX-shell env-var shape (upper-snake, starting with letter or underscore).\n`,
|
|
278
|
+
exitCode: 1,
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
if (isRefusedKey(key)) {
|
|
282
|
+
return {
|
|
283
|
+
kind: 'error',
|
|
284
|
+
stderr: `kindgi env refuses to manage "${key}" — reserved for framework runtime config.\nReserved prefixes: ${REFUSED_KEY_PREFIXES.join(', ')}.\nSet framework env directly (e.g. via your shell / secrets manager); .env.<envName> is for pack-level values.\n`,
|
|
285
|
+
exitCode: 1,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const runnersOutcome = pickRunners(ctx);
|
|
289
|
+
if (runnersOutcome.kind === 'error')
|
|
290
|
+
return runnersOutcome;
|
|
291
|
+
const runners = runnersOutcome.runners;
|
|
292
|
+
const resolved = await resolvePaths(ctx);
|
|
293
|
+
if (resolved.kind === 'error')
|
|
294
|
+
return resolved;
|
|
295
|
+
const paths = resolved.paths;
|
|
296
|
+
const view = await readFileView(ctx, runners, paths);
|
|
297
|
+
const raw = (await runners.readFile(paths.envFilePath)) ?? '';
|
|
298
|
+
if (ctx.options.force !== true) {
|
|
299
|
+
const refusal = refuseExistingKey(paths, view, key);
|
|
300
|
+
if (refusal !== undefined)
|
|
301
|
+
return refusal;
|
|
302
|
+
}
|
|
303
|
+
const next = trySetKey(raw, key, value);
|
|
304
|
+
if (next.kind === 'error')
|
|
305
|
+
return next;
|
|
306
|
+
await runners.writeFile(paths.envFilePath, next.contents);
|
|
307
|
+
const summary = {
|
|
308
|
+
envName: paths.envName,
|
|
309
|
+
envFilePath: paths.envFilePath,
|
|
310
|
+
key,
|
|
311
|
+
overwritten: next.overwritten,
|
|
312
|
+
created: raw === '',
|
|
313
|
+
};
|
|
314
|
+
const banner = [
|
|
315
|
+
'',
|
|
316
|
+
` ✓ ${next.overwritten ? 'Updated' : 'Wrote'} ${key} in ${paths.envFilePath}`,
|
|
317
|
+
'',
|
|
318
|
+
].join('\n');
|
|
319
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
320
|
+
return {
|
|
321
|
+
kind: 'ok',
|
|
322
|
+
rendered: { stdout: rendered.stdout, stderr: `${banner}\n` },
|
|
323
|
+
};
|
|
324
|
+
},
|
|
325
|
+
};
|
|
326
|
+
/** Refuse to change a key some env file already defines (unless --force). */
|
|
327
|
+
function refuseExistingKey(paths, view, key) {
|
|
328
|
+
if (!Object.hasOwn(view.values, key))
|
|
329
|
+
return undefined;
|
|
330
|
+
const definedIn = view.origin[key] ?? paths.envFilePath;
|
|
331
|
+
const effect = definedIn === paths.envFilePath
|
|
332
|
+
? 'overwrite it'
|
|
333
|
+
: `write an override to ${shown(paths, paths.envFilePath)}, which takes precedence`;
|
|
334
|
+
return {
|
|
335
|
+
kind: 'error',
|
|
336
|
+
stderr: `kindgi env set refuses to change ${key}, already set in ${shown(paths, definedIn)} ` +
|
|
337
|
+
`(value: ${redact(view.values[key] ?? '')}). Re-run with --force to ${effect}.\n`,
|
|
338
|
+
exitCode: 1,
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
function trySetKey(raw, key, value) {
|
|
342
|
+
try {
|
|
343
|
+
return { kind: 'ok', ...setKey(raw, key, value) };
|
|
344
|
+
}
|
|
345
|
+
catch (err) {
|
|
346
|
+
if (!(err instanceof EnvValueNotRepresentableError))
|
|
347
|
+
throw err;
|
|
348
|
+
return { kind: 'error', stderr: `kindgi env set: ${err.message}\n`, exitCode: 1 };
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
// ---------------------------------------------------------------------
|
|
352
|
+
// `kindgi env unset <KEY>`
|
|
353
|
+
// ---------------------------------------------------------------------
|
|
354
|
+
const unsetCmd = {
|
|
355
|
+
kind: 'leaf',
|
|
356
|
+
name: 'unset',
|
|
357
|
+
description: 'Remove KEY from .env.<envName>. Idempotent — no error if the key is absent.',
|
|
358
|
+
usage: 'kindgi env unset <KEY> [--env <name>] [--path <dir>]',
|
|
359
|
+
optionSpec: {
|
|
360
|
+
env: {
|
|
361
|
+
type: 'string',
|
|
362
|
+
description: "The environment: `local` is the project's own env files, any other name its `.env.<name>`. Default: `staging`.",
|
|
363
|
+
},
|
|
364
|
+
path: {
|
|
365
|
+
type: 'string',
|
|
366
|
+
description: 'The pack root, where the env files are. Default: the current directory.',
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
run: async (ctx) => {
|
|
370
|
+
const key = ctx.positionals[0];
|
|
371
|
+
if (typeof key !== 'string' || key === '') {
|
|
372
|
+
return {
|
|
373
|
+
kind: 'error',
|
|
374
|
+
stderr: 'Missing required argument: <KEY>.\n' + 'Usage: kindgi env unset <KEY> [--env <name>]\n',
|
|
375
|
+
exitCode: 1,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
if (isRefusedKey(key)) {
|
|
379
|
+
return {
|
|
380
|
+
kind: 'error',
|
|
381
|
+
stderr: `kindgi env refuses to manage "${key}" — reserved for framework runtime config.\n`,
|
|
382
|
+
exitCode: 1,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const runnersOutcome = pickRunners(ctx);
|
|
386
|
+
if (runnersOutcome.kind === 'error')
|
|
387
|
+
return runnersOutcome;
|
|
388
|
+
const runners = runnersOutcome.runners;
|
|
389
|
+
const resolved = await resolvePaths(ctx);
|
|
390
|
+
if (resolved.kind === 'error')
|
|
391
|
+
return resolved;
|
|
392
|
+
const paths = resolved.paths;
|
|
393
|
+
const raw = await runners.readFile(paths.envFilePath);
|
|
394
|
+
// Idempotent: a missing file means nothing to remove.
|
|
395
|
+
const next = raw === null ? { contents: '', removed: false } : unsetKey(raw, key);
|
|
396
|
+
if (raw !== null)
|
|
397
|
+
await runners.writeFile(paths.envFilePath, next.contents);
|
|
398
|
+
// Kindgi edits only the file it writes to. A lower-precedence file
|
|
399
|
+
// (e.g. the app's own `.env`) that still defines the key is left
|
|
400
|
+
// alone — say so, because the key is still set.
|
|
401
|
+
const after = await readFileView(ctx, runners, paths);
|
|
402
|
+
const stillDefinedIn = Object.hasOwn(after.values, key)
|
|
403
|
+
? shown(paths, after.origin[key] ?? '')
|
|
404
|
+
: undefined;
|
|
405
|
+
const summary = {
|
|
406
|
+
envName: paths.envName,
|
|
407
|
+
envFilePath: paths.envFilePath,
|
|
408
|
+
key,
|
|
409
|
+
removed: next.removed,
|
|
410
|
+
...(raw === null && { note: 'file did not exist' }),
|
|
411
|
+
...(stillDefinedIn !== undefined && { stillDefinedIn }),
|
|
412
|
+
};
|
|
413
|
+
const still = stillDefinedIn !== undefined
|
|
414
|
+
? ` ! ${key} is still set in ${stillDefinedIn} — kindgi env only edits ${shown(paths, paths.envFilePath)}.\n`
|
|
415
|
+
: '';
|
|
416
|
+
const outcome = raw === null
|
|
417
|
+
? `${paths.envFilePath} does not exist — no change.`
|
|
418
|
+
: next.removed
|
|
419
|
+
? `Removed ${key} from ${paths.envFilePath}`
|
|
420
|
+
: `${key} was not present in ${paths.envFilePath} — no change.`;
|
|
421
|
+
const banner = `\n ✓ ${outcome}\n${still}\n`;
|
|
422
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
423
|
+
return {
|
|
424
|
+
kind: 'ok',
|
|
425
|
+
rendered: { stdout: rendered.stdout, stderr: banner },
|
|
426
|
+
};
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
// ---------------------------------------------------------------------
|
|
430
|
+
// `kindgi env pull` — WIRED via /v1/env/*
|
|
431
|
+
// ---------------------------------------------------------------------
|
|
432
|
+
/**
|
|
433
|
+
* Fold pulled values into the write target. A key any env file already
|
|
434
|
+
* defines is skipped unless `overwrite`; runtime (`KINDGI_*`) and
|
|
435
|
+
* non-POSIX names are always skipped.
|
|
436
|
+
*/
|
|
437
|
+
function mergeRemote(raw, existing, remote, overwrite) {
|
|
438
|
+
let contents = raw;
|
|
439
|
+
const added = [];
|
|
440
|
+
const overwritten = [];
|
|
441
|
+
const skipped = [];
|
|
442
|
+
for (const rec of remote) {
|
|
443
|
+
const writable = !isRefusedKey(rec.name) && ENV_KEY_REGEX.test(rec.name);
|
|
444
|
+
if (!writable || (Object.hasOwn(existing, rec.name) && !overwrite)) {
|
|
445
|
+
skipped.push(rec.name);
|
|
446
|
+
continue;
|
|
447
|
+
}
|
|
448
|
+
const next = setKey(contents, rec.name, rec.value);
|
|
449
|
+
contents = next.contents;
|
|
450
|
+
(next.overwritten ? overwritten : added).push(rec.name);
|
|
451
|
+
}
|
|
452
|
+
return { contents, added, overwritten, skipped };
|
|
453
|
+
}
|
|
454
|
+
const pullCmd = {
|
|
455
|
+
kind: 'leaf',
|
|
456
|
+
name: 'pull',
|
|
457
|
+
description: 'Fetch remote env values via /v1/env/* and merge into .env.<envName>. Requires --scope.',
|
|
458
|
+
usage: 'kindgi env pull [--env <name>] --scope=<kind>[:id] [--path <dir>] [--overwrite-existing]',
|
|
459
|
+
optionSpec: {
|
|
460
|
+
env: {
|
|
461
|
+
type: 'string',
|
|
462
|
+
description: "The environment to pull, written to its `.env.<name>` (`local`: the project's own env files). Default: `staging`.",
|
|
463
|
+
},
|
|
464
|
+
path: {
|
|
465
|
+
type: 'string',
|
|
466
|
+
description: 'The pack root, where the env files are. Default: the current directory.',
|
|
467
|
+
},
|
|
468
|
+
scope: {
|
|
469
|
+
type: 'string',
|
|
470
|
+
description: 'Whose values to pull: `tenant`, `org:<orgId>` or `project:<projectId>`. Required.',
|
|
471
|
+
},
|
|
472
|
+
'overwrite-existing': {
|
|
473
|
+
type: 'boolean',
|
|
474
|
+
description: 'Replace keys an env file already sets. By default they are skipped.',
|
|
475
|
+
},
|
|
476
|
+
},
|
|
477
|
+
run: async (ctx) => {
|
|
478
|
+
const runnersOutcome = pickRunners(ctx);
|
|
479
|
+
if (runnersOutcome.kind === 'error')
|
|
480
|
+
return runnersOutcome;
|
|
481
|
+
const runners = runnersOutcome.runners;
|
|
482
|
+
const resolved = await resolvePaths(ctx);
|
|
483
|
+
if (resolved.kind === 'error')
|
|
484
|
+
return resolved;
|
|
485
|
+
const paths = resolved.paths;
|
|
486
|
+
const scopeResult = parseScope(stringFlag(ctx, 'scope'));
|
|
487
|
+
if (scopeResult.kind === 'err') {
|
|
488
|
+
return { kind: 'error', stderr: scopeResult.stderr, exitCode: 2 };
|
|
489
|
+
}
|
|
490
|
+
const overwrite = ctx.options['overwrite-existing'] === true;
|
|
491
|
+
try {
|
|
492
|
+
const remote = await pullAllPages(ctx, scopeResult.scope, paths.envName);
|
|
493
|
+
const raw = (await runners.readFile(paths.envFilePath)) ?? '';
|
|
494
|
+
const existing = (await readFileView(ctx, runners, paths)).values;
|
|
495
|
+
const { contents, added, overwritten, skipped } = mergeRemote(raw, existing, remote, overwrite);
|
|
496
|
+
if (contents !== raw) {
|
|
497
|
+
await runners.writeFile(paths.envFilePath, contents);
|
|
498
|
+
}
|
|
499
|
+
const summary = {
|
|
500
|
+
envName: paths.envName,
|
|
501
|
+
envFilePath: paths.envFilePath,
|
|
502
|
+
fetched: remote.length,
|
|
503
|
+
added: added.length,
|
|
504
|
+
overwritten: overwritten.length,
|
|
505
|
+
skipped: skipped.length,
|
|
506
|
+
keys: { added, overwritten, skipped },
|
|
507
|
+
};
|
|
508
|
+
const banner = [
|
|
509
|
+
'',
|
|
510
|
+
` Pulled ${remote.length} remote env value(s) for env=${paths.envName}, scope=${describeScope(scopeResult.scope)}.`,
|
|
511
|
+
` added: ${added.length}`,
|
|
512
|
+
` overwritten: ${overwritten.length} ${overwrite ? '' : '(pass --overwrite-existing to replace)'}`,
|
|
513
|
+
` skipped: ${skipped.length}`,
|
|
514
|
+
'',
|
|
515
|
+
].join('\n');
|
|
516
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
517
|
+
return {
|
|
518
|
+
kind: 'ok',
|
|
519
|
+
rendered: { stdout: rendered.stdout, stderr: `${banner}\n` },
|
|
520
|
+
};
|
|
521
|
+
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
return commandResultFromThrown(err, ctx, 'kindgi env pull');
|
|
524
|
+
}
|
|
525
|
+
},
|
|
526
|
+
};
|
|
527
|
+
async function pullAllPages(ctx, scope, envName) {
|
|
528
|
+
const out = [];
|
|
529
|
+
let cursor;
|
|
530
|
+
const env = ctx.client().env;
|
|
531
|
+
for (let i = 0; i < 1_000; i += 1) {
|
|
532
|
+
const page = await env.list({
|
|
533
|
+
scope,
|
|
534
|
+
envName,
|
|
535
|
+
...(cursor !== undefined && { cursor: cursor }),
|
|
536
|
+
});
|
|
537
|
+
for (const rec of page.data)
|
|
538
|
+
out.push({ name: rec.name, value: rec.value });
|
|
539
|
+
if (page.nextCursor === undefined)
|
|
540
|
+
break;
|
|
541
|
+
cursor = page.nextCursor;
|
|
542
|
+
}
|
|
543
|
+
return out;
|
|
544
|
+
}
|
|
545
|
+
function parseScope(raw) {
|
|
546
|
+
if (raw === undefined) {
|
|
547
|
+
return {
|
|
548
|
+
kind: 'err',
|
|
549
|
+
stderr: 'Missing required flag: --scope=<kind>[:id].\n' +
|
|
550
|
+
'Accepted forms:\n --scope=tenant\n --scope=org:<orgId>\n --scope=project:<projectId>\n',
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
const [kind, id] = raw.split(':');
|
|
554
|
+
const tenantId = 'session-tenant';
|
|
555
|
+
if (kind === 'tenant') {
|
|
556
|
+
if (id !== undefined && id !== '') {
|
|
557
|
+
return { kind: 'err', stderr: `Malformed --scope: tenant carries no id, got "${raw}".\n` };
|
|
558
|
+
}
|
|
559
|
+
return { kind: 'ok', scope: { kind: 'tenant', tenantId } };
|
|
560
|
+
}
|
|
561
|
+
if (kind === 'org') {
|
|
562
|
+
if (id === undefined || id === '') {
|
|
563
|
+
return {
|
|
564
|
+
kind: 'err',
|
|
565
|
+
stderr: `Malformed --scope: expected --scope=org:<orgId>, got "${raw}".\n`,
|
|
566
|
+
};
|
|
567
|
+
}
|
|
568
|
+
return { kind: 'ok', scope: { kind: 'org', tenantId, orgId: id } };
|
|
569
|
+
}
|
|
570
|
+
if (kind === 'project') {
|
|
571
|
+
if (id === undefined || id === '') {
|
|
572
|
+
return {
|
|
573
|
+
kind: 'err',
|
|
574
|
+
stderr: `Malformed --scope: expected --scope=project:<projectId>, got "${raw}".\n`,
|
|
575
|
+
};
|
|
576
|
+
}
|
|
577
|
+
return { kind: 'ok', scope: { kind: 'project', tenantId, projectId: id } };
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
kind: 'err',
|
|
581
|
+
stderr: `Unknown --scope kind "${String(kind)}". Accepted: tenant, org:<orgId>, project:<projectId>.\n`,
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
function describeScope(scope) {
|
|
585
|
+
if (scope.kind === 'tenant')
|
|
586
|
+
return 'tenant';
|
|
587
|
+
if (scope.kind === 'org')
|
|
588
|
+
return `org:${scope.orgId}`;
|
|
589
|
+
return `project:${scope.projectId}`;
|
|
590
|
+
}
|
|
591
|
+
const SECRETS_BACKEND_CHOICES = ['none', 'postgres', 'secret-manager'];
|
|
592
|
+
const SECRETS_BACKEND_KMS_CHOICES = ['gcp', 'aws', 'libsodium', 'vault'];
|
|
593
|
+
const initCmd = {
|
|
594
|
+
kind: 'leaf',
|
|
595
|
+
name: 'init',
|
|
596
|
+
description: 'Scaffold `.env.example` for a deployment target. Interactive prompt when flags omit an axis and stdin is a TTY.',
|
|
597
|
+
usage: 'kindgi env init [--secrets-backend=<none|postgres|secret-manager>] [--kms=<gcp|aws|libsodium|vault>] [--out=<path>] [--force] [--non-interactive]',
|
|
598
|
+
optionSpec: {
|
|
599
|
+
'secrets-backend': {
|
|
600
|
+
type: 'string',
|
|
601
|
+
description: "The deployment's secrets backend: `none`, `postgres` or `secret-manager`. Asked for when omitted on a terminal.",
|
|
602
|
+
},
|
|
603
|
+
kms: {
|
|
604
|
+
type: 'string',
|
|
605
|
+
description: 'The KMS for the `postgres` or `secret-manager` backend: `gcp`, `aws`, `libsodium` or `vault`. Asked for when needed and omitted.',
|
|
606
|
+
},
|
|
607
|
+
out: {
|
|
608
|
+
type: 'string',
|
|
609
|
+
description: 'Where to write the file. Default: `.env.example` in the current directory.',
|
|
610
|
+
},
|
|
611
|
+
force: {
|
|
612
|
+
type: 'boolean',
|
|
613
|
+
description: 'Overwrite an existing file at the output path. By default `init` refuses.',
|
|
614
|
+
},
|
|
615
|
+
'non-interactive': {
|
|
616
|
+
type: 'boolean',
|
|
617
|
+
description: 'Never prompt: fail when `--secrets-backend`, or a needed `--kms`, is missing.',
|
|
618
|
+
},
|
|
619
|
+
},
|
|
620
|
+
run: async (ctx) => {
|
|
621
|
+
const runnersRes = pickRunners(ctx);
|
|
622
|
+
if (runnersRes.kind === 'error')
|
|
623
|
+
return runnersRes;
|
|
624
|
+
const backendFlag = stringFlag(ctx, 'secrets-backend');
|
|
625
|
+
const kmsFlag = stringFlag(ctx, 'kms');
|
|
626
|
+
const outFlag = stringFlag(ctx, 'out');
|
|
627
|
+
const outPath = outFlag !== undefined && outFlag !== '' ? outFlag : join(ctx.cwd, '.env.example');
|
|
628
|
+
const nonInteractive = ctx.options['non-interactive'] === true;
|
|
629
|
+
// Validate flag values up-front.
|
|
630
|
+
if (backendFlag !== undefined && !SECRETS_BACKEND_CHOICES.includes(backendFlag)) {
|
|
631
|
+
return {
|
|
632
|
+
kind: 'error',
|
|
633
|
+
stderr: `--secrets-backend must be one of: ${SECRETS_BACKEND_CHOICES.join(', ')}. Got "${backendFlag}".\n`,
|
|
634
|
+
exitCode: 2,
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
if (kmsFlag !== undefined && !SECRETS_BACKEND_KMS_CHOICES.includes(kmsFlag)) {
|
|
638
|
+
return {
|
|
639
|
+
kind: 'error',
|
|
640
|
+
stderr: `--kms must be one of: ${SECRETS_BACKEND_KMS_CHOICES.join(', ')}. Got "${kmsFlag}".\n`,
|
|
641
|
+
exitCode: 2,
|
|
642
|
+
};
|
|
643
|
+
}
|
|
644
|
+
// Resolve missing axes via interactive prompt or fail loud.
|
|
645
|
+
const seam = ctx.envInitInputSeam ?? {};
|
|
646
|
+
const stdinIsTty = seam.stdinIsTty ?? (() => Boolean(process.stdin.isTTY));
|
|
647
|
+
let backend = backendFlag;
|
|
648
|
+
let kms = kmsFlag;
|
|
649
|
+
if (backend === undefined) {
|
|
650
|
+
if (nonInteractive || !stdinIsTty()) {
|
|
651
|
+
return {
|
|
652
|
+
kind: 'error',
|
|
653
|
+
stderr: `--secrets-backend is required in non-interactive mode. Choices: ${SECRETS_BACKEND_CHOICES.join(', ')}.\n`,
|
|
654
|
+
exitCode: 2,
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
const pick = await promptOnce(seam, 'Which secrets backend?', SECRETS_BACKEND_CHOICES);
|
|
658
|
+
if (pick === undefined) {
|
|
659
|
+
return { kind: 'error', stderr: 'Cancelled.\n', exitCode: 1 };
|
|
660
|
+
}
|
|
661
|
+
backend = pick;
|
|
662
|
+
}
|
|
663
|
+
// Only prompt for KMS if backend needs it.
|
|
664
|
+
const needsKms = backend === 'postgres' || backend === 'secret-manager';
|
|
665
|
+
if (needsKms && kms === undefined) {
|
|
666
|
+
if (nonInteractive || !stdinIsTty()) {
|
|
667
|
+
return {
|
|
668
|
+
kind: 'error',
|
|
669
|
+
stderr: `--kms is required when --secrets-backend=${backend}. Choices: ${SECRETS_BACKEND_KMS_CHOICES.join(', ')}.\n`,
|
|
670
|
+
exitCode: 2,
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
const pick = await promptOnce(seam, `Which KMS / vendor for backend=${backend}?`, SECRETS_BACKEND_KMS_CHOICES);
|
|
674
|
+
if (pick === undefined) {
|
|
675
|
+
return { kind: 'error', stderr: 'Cancelled.\n', exitCode: 1 };
|
|
676
|
+
}
|
|
677
|
+
kms = pick;
|
|
678
|
+
}
|
|
679
|
+
const target = {
|
|
680
|
+
...(backend !== undefined && { secretsBackend: backend }),
|
|
681
|
+
...(kms !== undefined && { secretsBackendKms: kms }),
|
|
682
|
+
};
|
|
683
|
+
// Refuse to overwrite an existing file unless --force.
|
|
684
|
+
if (ctx.options.force !== true) {
|
|
685
|
+
const existing = await runnersRes.runners.readFile(outPath);
|
|
686
|
+
if (existing !== null) {
|
|
687
|
+
return {
|
|
688
|
+
kind: 'error',
|
|
689
|
+
stderr: `Refusing to overwrite existing ${outPath}. Pass --force to replace it.\n`,
|
|
690
|
+
exitCode: 1,
|
|
691
|
+
};
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
const vars = envVarsForTarget(target);
|
|
695
|
+
const body = renderEnvExample(vars);
|
|
696
|
+
try {
|
|
697
|
+
await runnersRes.runners.writeFile(outPath, body);
|
|
698
|
+
}
|
|
699
|
+
catch (err) {
|
|
700
|
+
return commandResultFromThrown(err, ctx, 'kindgi env init');
|
|
701
|
+
}
|
|
702
|
+
const summary = {
|
|
703
|
+
outPath,
|
|
704
|
+
target,
|
|
705
|
+
varsWritten: vars.length,
|
|
706
|
+
};
|
|
707
|
+
return {
|
|
708
|
+
kind: 'ok',
|
|
709
|
+
rendered: {
|
|
710
|
+
stdout: renderJson(summary, ctx.globals.format).stdout,
|
|
711
|
+
stderr: `\n Wrote ${vars.length} env-var entries → ${outPath}\n Target: ${JSON.stringify(target)}\n\n`,
|
|
712
|
+
},
|
|
713
|
+
};
|
|
714
|
+
},
|
|
715
|
+
};
|
|
716
|
+
async function promptOnce(seam, question, choices) {
|
|
717
|
+
if (seam.promptChoice !== undefined) {
|
|
718
|
+
return seam.promptChoice(question, choices);
|
|
719
|
+
}
|
|
720
|
+
return realPromptChoice(question, choices);
|
|
721
|
+
}
|
|
722
|
+
async function realPromptChoice(question, choices) {
|
|
723
|
+
const rl = readline.createInterface({
|
|
724
|
+
input: process.stdin,
|
|
725
|
+
output: process.stderr,
|
|
726
|
+
terminal: true,
|
|
727
|
+
});
|
|
728
|
+
try {
|
|
729
|
+
process.stderr.write(`\n${question}\n`);
|
|
730
|
+
for (let i = 0; i < choices.length; i += 1) {
|
|
731
|
+
process.stderr.write(` ${i + 1}) ${choices[i]}\n`);
|
|
732
|
+
}
|
|
733
|
+
const answer = await new Promise((resolve) => {
|
|
734
|
+
rl.question(`Choose [1-${choices.length}]: `, (raw) => resolve(raw));
|
|
735
|
+
});
|
|
736
|
+
const trimmed = answer.trim();
|
|
737
|
+
// Accept either the number (1-based) or the exact string.
|
|
738
|
+
const asNumber = Number.parseInt(trimmed, 10);
|
|
739
|
+
if (Number.isFinite(asNumber) && asNumber >= 1 && asNumber <= choices.length) {
|
|
740
|
+
return choices[asNumber - 1];
|
|
741
|
+
}
|
|
742
|
+
if (choices.includes(trimmed))
|
|
743
|
+
return trimmed;
|
|
744
|
+
return undefined;
|
|
745
|
+
}
|
|
746
|
+
finally {
|
|
747
|
+
rl.close();
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
// ---------------------------------------------------------------------
|
|
751
|
+
// Group registration
|
|
752
|
+
// ---------------------------------------------------------------------
|
|
753
|
+
// ---------------------------------------------------------------------
|
|
754
|
+
// `kindgi env plan`
|
|
755
|
+
// ---------------------------------------------------------------------
|
|
756
|
+
const planCmd = {
|
|
757
|
+
kind: 'leaf',
|
|
758
|
+
name: 'plan',
|
|
759
|
+
description: 'Show the process env a deployed pack service gets in --env: each name the pack declares (env.required / env.optional) and its value or Secret Manager reference from environments.<name>.env. Prints Terraform input (default) or gcloud flags; exits 1 when a required name has no value or a secret is given in the clear.',
|
|
760
|
+
usage: 'kindgi env plan [--env <name>] [--format=terraform|gcloud] [--path <dir>]',
|
|
761
|
+
optionSpec: {
|
|
762
|
+
env: {
|
|
763
|
+
type: 'string',
|
|
764
|
+
description: 'The environment to plan, from `environments.<name>.env` in `kindgi.config.ts`. Default: `staging`.',
|
|
765
|
+
},
|
|
766
|
+
format: {
|
|
767
|
+
type: 'string',
|
|
768
|
+
description: 'The output: `terraform` (default) for Terraform input, or `gcloud` for `--set-env-vars` / `--set-secrets` flags.',
|
|
769
|
+
},
|
|
770
|
+
path: {
|
|
771
|
+
type: 'string',
|
|
772
|
+
description: 'The pack root, where `kindgi.config.ts` is. Default: the current directory.',
|
|
773
|
+
},
|
|
774
|
+
},
|
|
775
|
+
run: async (ctx) => {
|
|
776
|
+
const format = stringFlag(ctx, 'format') ?? 'terraform';
|
|
777
|
+
if (format !== 'terraform' && format !== 'gcloud') {
|
|
778
|
+
return {
|
|
779
|
+
kind: 'error',
|
|
780
|
+
stderr: `kindgi env plan: --format is terraform or gcloud, not "${format}"\n`,
|
|
781
|
+
exitCode: 1,
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
const resolved = await resolvePaths(ctx);
|
|
785
|
+
if (resolved.kind === 'error')
|
|
786
|
+
return resolved;
|
|
787
|
+
const planned = planPackEnv(resolved.paths.config, resolved.paths.envName);
|
|
788
|
+
if (planned.kind === 'err') {
|
|
789
|
+
return { kind: 'error', stderr: `kindgi env plan: ${planned.message}\n`, exitCode: 1 };
|
|
790
|
+
}
|
|
791
|
+
const plan = planned.plan;
|
|
792
|
+
const stdout = format === 'terraform' ? renderTerraform(plan) : renderGcloud(plan);
|
|
793
|
+
const stderr = `${describePackEnvPlan(plan).join('\n')}\n`;
|
|
794
|
+
const blocked = plan.missing.length > 0 || plan.problems.length > 0;
|
|
795
|
+
return blocked
|
|
796
|
+
? { kind: 'error', stderr, exitCode: 1 }
|
|
797
|
+
: { kind: 'ok', rendered: { stdout, stderr } };
|
|
798
|
+
},
|
|
799
|
+
};
|
|
800
|
+
export const envCommand = {
|
|
801
|
+
kind: 'group',
|
|
802
|
+
name: 'env',
|
|
803
|
+
description: 'Manage per-environment values that resolve into `needs.env` at deploy time.',
|
|
804
|
+
subcommands: [listCmd, setCmd, unsetCmd, pullCmd, initCmd, planCmd],
|
|
805
|
+
};
|
|
806
|
+
// ---------------------------------------------------------------------
|
|
807
|
+
// Helpers
|
|
808
|
+
// ---------------------------------------------------------------------
|
|
809
|
+
function redact(value) {
|
|
810
|
+
if (value.length === 0)
|
|
811
|
+
return '(empty)';
|
|
812
|
+
if (value.length <= 4)
|
|
813
|
+
return '*'.repeat(value.length);
|
|
814
|
+
return `${value.slice(0, 2)}${'*'.repeat(Math.max(value.length - 4, 3))}${value.slice(-2)}`;
|
|
815
|
+
}
|
|
816
|
+
//# sourceMappingURL=env.js.map
|