@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,750 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
/**
|
|
4
|
+
* `kindgi secrets` — full lifecycle over the API's `/v1/secrets/*`
|
|
5
|
+
* routes via the SDK's `SecretsClient`.
|
|
6
|
+
*
|
|
7
|
+
* Every verb requires `--env=<name>` + `--scope=<kind>[:id]`. Scope is
|
|
8
|
+
* mandatory until the CLI resolves a global `--project`. Value input on `set` and `rotate` follows a strict
|
|
9
|
+
* fail-loud contract:
|
|
10
|
+
*
|
|
11
|
+
* - **Default:** interactive TTY prompt with echo disabled + one-time
|
|
12
|
+
* confirmation. Rejects if stdin is not a TTY.
|
|
13
|
+
* - `--from-stdin`: read from stdin (for pipelines / CI).
|
|
14
|
+
* - `--from-file <path>`: read from file; rejects if file mode includes
|
|
15
|
+
* group/world-read (fs.stat().mode & 0o077 !== 0).
|
|
16
|
+
*
|
|
17
|
+
* Never echoes values. Success messages carry NAME + scope + env only.
|
|
18
|
+
*/
|
|
19
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
20
|
+
import { EOL } from 'node:os';
|
|
21
|
+
import * as readline from 'node:readline';
|
|
22
|
+
import { renderJson } from '../output.js';
|
|
23
|
+
import { commandResultFromThrown, integerFlag, requiredPositional, stringFlag } from './helpers.js';
|
|
24
|
+
// ---------------------------------------------------------------------
|
|
25
|
+
// Shared option spec — every leaf command carries these plus its own.
|
|
26
|
+
// ---------------------------------------------------------------------
|
|
27
|
+
const SCOPE_OPTION_SPEC = {
|
|
28
|
+
env: {
|
|
29
|
+
type: 'string',
|
|
30
|
+
description: "The environment the secret belongs to. Required; under `kindgi dev`, `local` is the pack's env files.",
|
|
31
|
+
},
|
|
32
|
+
scope: {
|
|
33
|
+
type: 'string',
|
|
34
|
+
description: 'Where the secret lives: `tenant`, `org:<orgId>` or `project:<projectId>`. Required.',
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Parse `--scope=<kind>[:id]`. Every value the CLI accepts
|
|
39
|
+
* maps to a `Scope` from `@kindgi/platform`. `tenantId` is
|
|
40
|
+
* synthesized as a placeholder — the server derives the real tenantId
|
|
41
|
+
* from the session bearer; the CLI just needs the discriminant + id.
|
|
42
|
+
*/
|
|
43
|
+
function parseScopeAndEnv(ctx) {
|
|
44
|
+
const envName = stringFlag(ctx, 'env');
|
|
45
|
+
if (envName === undefined) {
|
|
46
|
+
return {
|
|
47
|
+
kind: 'err',
|
|
48
|
+
stderr: 'Missing required flag: --env=<name>.\nEvery `kindgi secrets` command requires --env; secrets are per-environment.\n',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
const rawScope = stringFlag(ctx, 'scope');
|
|
52
|
+
if (rawScope === undefined) {
|
|
53
|
+
return {
|
|
54
|
+
kind: 'err',
|
|
55
|
+
stderr: 'Missing required flag: --scope=<kind>[:id].\nAccepted forms:\n --scope=tenant\n --scope=org:<orgId>\n --scope=project:<projectId>\n(--scope is required: there is no default scope yet.)\n',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const [kind, id] = rawScope.split(':');
|
|
59
|
+
const tenantId = 'session-tenant';
|
|
60
|
+
if (kind === 'tenant') {
|
|
61
|
+
if (id !== undefined && id !== '') {
|
|
62
|
+
return {
|
|
63
|
+
kind: 'err',
|
|
64
|
+
stderr: `Malformed --scope: tenant scope carries no id, got "${rawScope}".\n`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
kind: 'ok',
|
|
69
|
+
value: { scope: { kind: 'tenant', tenantId }, envName: envName },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
if (kind === 'org') {
|
|
73
|
+
if (id === undefined || id === '') {
|
|
74
|
+
return {
|
|
75
|
+
kind: 'err',
|
|
76
|
+
stderr: `Malformed --scope: expected --scope=org:<orgId>, got "${rawScope}".\n`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
kind: 'ok',
|
|
81
|
+
value: {
|
|
82
|
+
scope: { kind: 'org', tenantId, orgId: id },
|
|
83
|
+
envName: envName,
|
|
84
|
+
},
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (kind === 'project') {
|
|
88
|
+
if (id === undefined || id === '') {
|
|
89
|
+
return {
|
|
90
|
+
kind: 'err',
|
|
91
|
+
stderr: `Malformed --scope: expected --scope=project:<projectId>, got "${rawScope}".\n`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
kind: 'ok',
|
|
96
|
+
value: {
|
|
97
|
+
scope: { kind: 'project', tenantId, projectId: id },
|
|
98
|
+
envName: envName,
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
kind: 'err',
|
|
104
|
+
stderr: `Unknown --scope kind "${String(kind)}".\nAccepted: tenant, org:<orgId>, project:<projectId>.\n`,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Read a secret value from one of three sources per canonical
|
|
109
|
+
* decision #2. Result carries the raw bytes only; callers must never
|
|
110
|
+
* echo or log the return value.
|
|
111
|
+
*/
|
|
112
|
+
async function readValueInput(cfg) {
|
|
113
|
+
const chosen = [cfg.fromStdin, cfg.fromFile !== undefined].filter(Boolean).length;
|
|
114
|
+
if (chosen > 1) {
|
|
115
|
+
return {
|
|
116
|
+
kind: 'err',
|
|
117
|
+
stderr: 'Mutually exclusive flags: --from-stdin and --from-file cannot be combined.\n',
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
if (cfg.fromFile !== undefined) {
|
|
121
|
+
const path = cfg.fromFile;
|
|
122
|
+
const statImpl = cfg.statFile ?? (async (p) => await stat(p));
|
|
123
|
+
let info;
|
|
124
|
+
try {
|
|
125
|
+
info = await statImpl(path);
|
|
126
|
+
}
|
|
127
|
+
catch (err) {
|
|
128
|
+
return {
|
|
129
|
+
kind: 'err',
|
|
130
|
+
stderr: `Cannot read --from-file "${path}": ${err.message}\n`,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// Reject if group/world can read (mode bits 0o077 nonzero).
|
|
134
|
+
if ((info.mode & 0o077) !== 0) {
|
|
135
|
+
const octal = (info.mode & 0o777).toString(8).padStart(4, '0');
|
|
136
|
+
return {
|
|
137
|
+
kind: 'err',
|
|
138
|
+
stderr: `Refusing to read "${path}": file mode ${octal} allows group/world read. ` +
|
|
139
|
+
`Restrict to 0600 (chmod 600 ${path}) and retry.\nSecrets on disk must be readable by their owner only.\n`,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
const readImpl = cfg.readFileAt ?? (async (p) => await readFile(p, 'utf8'));
|
|
143
|
+
try {
|
|
144
|
+
const raw = await readImpl(path);
|
|
145
|
+
return { kind: 'ok', value: stripTrailingNewline(raw) };
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
return { kind: 'err', stderr: `Failed to read "${path}": ${err.message}\n` };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (cfg.fromStdin) {
|
|
152
|
+
const reader = cfg.readStdin ?? readStdinToEnd;
|
|
153
|
+
try {
|
|
154
|
+
const raw = await reader();
|
|
155
|
+
return { kind: 'ok', value: stripTrailingNewline(raw) };
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
return { kind: 'err', stderr: `Failed to read stdin: ${err.message}\n` };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
// Default: TTY prompt.
|
|
162
|
+
const stdinIsTty = cfg.stdinIsTty ?? (() => Boolean(process.stdin.isTTY));
|
|
163
|
+
if (!stdinIsTty()) {
|
|
164
|
+
return {
|
|
165
|
+
kind: 'err',
|
|
166
|
+
stderr: 'Refusing to read from non-TTY stdin without --from-stdin. ' +
|
|
167
|
+
'Pipe the value via `--from-stdin`, or use `--from-file <path>` (mode 0600), or run interactively.\n',
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const tty = cfg.tty ?? realTtySeam();
|
|
171
|
+
try {
|
|
172
|
+
const first = await tty.promptHidden('Value: ');
|
|
173
|
+
const second = await tty.promptHidden('Re-enter to confirm: ');
|
|
174
|
+
if (first !== second) {
|
|
175
|
+
return { kind: 'err', stderr: 'Values did not match. Aborted.\n' };
|
|
176
|
+
}
|
|
177
|
+
if (first.length === 0) {
|
|
178
|
+
return { kind: 'err', stderr: 'Empty value refused.\n' };
|
|
179
|
+
}
|
|
180
|
+
return { kind: 'ok', value: first };
|
|
181
|
+
}
|
|
182
|
+
finally {
|
|
183
|
+
tty.close();
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function stripTrailingNewline(s) {
|
|
187
|
+
if (s.endsWith(EOL))
|
|
188
|
+
return s.slice(0, -EOL.length);
|
|
189
|
+
if (s.endsWith('\n'))
|
|
190
|
+
return s.slice(0, -1);
|
|
191
|
+
return s;
|
|
192
|
+
}
|
|
193
|
+
async function readStdinToEnd() {
|
|
194
|
+
const chunks = [];
|
|
195
|
+
for await (const chunk of process.stdin) {
|
|
196
|
+
chunks.push(chunk);
|
|
197
|
+
}
|
|
198
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
199
|
+
}
|
|
200
|
+
function realTtySeam() {
|
|
201
|
+
const rl = readline.createInterface({
|
|
202
|
+
input: process.stdin,
|
|
203
|
+
output: process.stderr,
|
|
204
|
+
terminal: true,
|
|
205
|
+
});
|
|
206
|
+
// Silence output while user types. `readline` doesn't expose a
|
|
207
|
+
// built-in "no-echo" mode; the standard trick is to override the
|
|
208
|
+
// internal `_writeToOutput` on the interface. This is portable
|
|
209
|
+
// enough across Node 22.x + prints only the prompt itself.
|
|
210
|
+
const rlAny = rl;
|
|
211
|
+
return {
|
|
212
|
+
promptHidden: (prompt) => new Promise((resolve) => {
|
|
213
|
+
const originalWrite = rlAny._writeToOutput;
|
|
214
|
+
rlAny._writeToOutput = function overrideWrite(str) {
|
|
215
|
+
// Write only the prompt itself (rl.question emits the
|
|
216
|
+
// prompt via the same channel); swallow keystrokes.
|
|
217
|
+
if (str === prompt) {
|
|
218
|
+
(rlAny.output ?? process.stderr).write(str);
|
|
219
|
+
}
|
|
220
|
+
};
|
|
221
|
+
rl.question(prompt, (answer) => {
|
|
222
|
+
if (originalWrite !== undefined) {
|
|
223
|
+
rlAny._writeToOutput = originalWrite;
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
// biome-ignore lint/performance/noDelete: readline distinguishes missing property from undefined
|
|
227
|
+
delete rlAny._writeToOutput;
|
|
228
|
+
}
|
|
229
|
+
(rlAny.output ?? process.stderr).write('\n');
|
|
230
|
+
resolve(answer);
|
|
231
|
+
});
|
|
232
|
+
}),
|
|
233
|
+
close: () => {
|
|
234
|
+
rl.close();
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
// ---------------------------------------------------------------------
|
|
239
|
+
// SDK client access
|
|
240
|
+
// ---------------------------------------------------------------------
|
|
241
|
+
function secretsFrom(ctx) {
|
|
242
|
+
return ctx.client().secrets;
|
|
243
|
+
}
|
|
244
|
+
// ---------------------------------------------------------------------
|
|
245
|
+
// list
|
|
246
|
+
// ---------------------------------------------------------------------
|
|
247
|
+
const listCmd = {
|
|
248
|
+
kind: 'leaf',
|
|
249
|
+
name: 'list',
|
|
250
|
+
description: 'List secret metadata under --scope + --env. Never returns values.',
|
|
251
|
+
usage: 'kindgi secrets list --env=<name> --scope=<kind>[:id] [--include-revoked] [--name-prefix=<p>] [--cursor=<c>] [--limit=<n>]',
|
|
252
|
+
optionSpec: {
|
|
253
|
+
...SCOPE_OPTION_SPEC,
|
|
254
|
+
'include-revoked': {
|
|
255
|
+
type: 'boolean',
|
|
256
|
+
description: 'Include revoked secrets. Not applied yet: the API ignores it, so revoked secrets stay hidden.',
|
|
257
|
+
},
|
|
258
|
+
'name-prefix': {
|
|
259
|
+
type: 'string',
|
|
260
|
+
description: 'Only the secrets whose name starts with this prefix.',
|
|
261
|
+
},
|
|
262
|
+
cursor: {
|
|
263
|
+
type: 'string',
|
|
264
|
+
description: "Resume after this cursor, from the previous page's `nextCursor`.",
|
|
265
|
+
},
|
|
266
|
+
limit: {
|
|
267
|
+
type: 'string',
|
|
268
|
+
description: 'The most secrets to return (default 25, at most 100).',
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
run: async (ctx) => {
|
|
272
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
273
|
+
if (parsed.kind === 'err')
|
|
274
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
275
|
+
try {
|
|
276
|
+
const page = await secretsFrom(ctx).list({
|
|
277
|
+
scope: parsed.value.scope,
|
|
278
|
+
envName: parsed.value.envName,
|
|
279
|
+
...(ctx.options['include-revoked'] === true && { includeRevoked: true }),
|
|
280
|
+
...(stringFlag(ctx, 'name-prefix') !== undefined && {
|
|
281
|
+
namePrefix: stringFlag(ctx, 'name-prefix'),
|
|
282
|
+
}),
|
|
283
|
+
...(stringFlag(ctx, 'cursor') !== undefined && {
|
|
284
|
+
cursor: stringFlag(ctx, 'cursor'),
|
|
285
|
+
}),
|
|
286
|
+
...(stringFlag(ctx, 'limit') !== undefined && {
|
|
287
|
+
limit: integerFlag(ctx, 'limit'),
|
|
288
|
+
}),
|
|
289
|
+
});
|
|
290
|
+
return { kind: 'ok', rendered: renderJson(page, ctx.globals.format) };
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets list');
|
|
294
|
+
}
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
// ---------------------------------------------------------------------
|
|
298
|
+
// get
|
|
299
|
+
// ---------------------------------------------------------------------
|
|
300
|
+
const getCmd = {
|
|
301
|
+
kind: 'leaf',
|
|
302
|
+
name: 'get',
|
|
303
|
+
description: 'Fetch secret metadata (name + version + timestamps only, NEVER the value).',
|
|
304
|
+
usage: 'kindgi secrets get <NAME> --env=<name> --scope=<kind>[:id]',
|
|
305
|
+
optionSpec: SCOPE_OPTION_SPEC,
|
|
306
|
+
run: async (ctx) => {
|
|
307
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
308
|
+
if (parsed.kind === 'err')
|
|
309
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
310
|
+
let name;
|
|
311
|
+
try {
|
|
312
|
+
name = requiredPositional(ctx, 0, 'NAME');
|
|
313
|
+
}
|
|
314
|
+
catch (err) {
|
|
315
|
+
return { kind: 'error', stderr: `${err.message}\n`, exitCode: 2 };
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
const rec = await secretsFrom(ctx).get({
|
|
319
|
+
scope: parsed.value.scope,
|
|
320
|
+
envName: parsed.value.envName,
|
|
321
|
+
name,
|
|
322
|
+
});
|
|
323
|
+
if (rec === null) {
|
|
324
|
+
return {
|
|
325
|
+
kind: 'error',
|
|
326
|
+
stderr: `No secret "${name}" at scope ${describeScope(parsed.value.scope)} in env "${parsed.value.envName}".\n`,
|
|
327
|
+
exitCode: 1,
|
|
328
|
+
};
|
|
329
|
+
}
|
|
330
|
+
return { kind: 'ok', rendered: renderJson(sanitizeSecret(rec), ctx.globals.format) };
|
|
331
|
+
}
|
|
332
|
+
catch (err) {
|
|
333
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets get');
|
|
334
|
+
}
|
|
335
|
+
},
|
|
336
|
+
};
|
|
337
|
+
function valueInputSeamFromCtx(ctx) {
|
|
338
|
+
return ctx.secretsInputSeam ?? {};
|
|
339
|
+
}
|
|
340
|
+
const setCmd = {
|
|
341
|
+
kind: 'leaf',
|
|
342
|
+
name: 'set',
|
|
343
|
+
description: 'Write a new secret value. Default: interactive TTY prompt. Use --from-stdin | --from-file for automation.',
|
|
344
|
+
usage: 'kindgi secrets set <NAME> --env=<name> --scope=<kind>[:id] [--write-mode=create-new|add-version] [--from-stdin | --from-file <path>] [--rotation-due-at=<iso>] [--if-version=<n>]',
|
|
345
|
+
optionSpec: {
|
|
346
|
+
...SCOPE_OPTION_SPEC,
|
|
347
|
+
'from-stdin': {
|
|
348
|
+
type: 'boolean',
|
|
349
|
+
description: "Read the secret's value from stdin instead of prompting.",
|
|
350
|
+
},
|
|
351
|
+
'from-file': {
|
|
352
|
+
type: 'string',
|
|
353
|
+
description: 'Read the value from this file instead of prompting. A file group or others can access is refused (`chmod 600`).',
|
|
354
|
+
},
|
|
355
|
+
'write-mode': {
|
|
356
|
+
type: 'string',
|
|
357
|
+
description: '`create-new` (the default) refuses a secret that exists; `add-version` writes a new version, creating it if needed.',
|
|
358
|
+
},
|
|
359
|
+
'rotation-due-at': {
|
|
360
|
+
type: 'string',
|
|
361
|
+
description: 'When the secret is due for rotation, as an ISO 8601 timestamp; kept with its metadata.',
|
|
362
|
+
},
|
|
363
|
+
'if-version': {
|
|
364
|
+
type: 'string',
|
|
365
|
+
description: "Write only if the secret is still at this version (`0` when it doesn't exist yet); otherwise fail with a conflict.",
|
|
366
|
+
},
|
|
367
|
+
},
|
|
368
|
+
run: async (ctx) => {
|
|
369
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
370
|
+
if (parsed.kind === 'err')
|
|
371
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
372
|
+
let name;
|
|
373
|
+
try {
|
|
374
|
+
name = requiredPositional(ctx, 0, 'NAME');
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
return { kind: 'error', stderr: `${err.message}\n`, exitCode: 2 };
|
|
378
|
+
}
|
|
379
|
+
const seam = valueInputSeamFromCtx(ctx);
|
|
380
|
+
const valueRes = await readValueInput({
|
|
381
|
+
fromStdin: ctx.options['from-stdin'] === true,
|
|
382
|
+
fromFile: stringFlag(ctx, 'from-file'),
|
|
383
|
+
...seam,
|
|
384
|
+
});
|
|
385
|
+
if (valueRes.kind === 'err') {
|
|
386
|
+
return { kind: 'error', stderr: valueRes.stderr, exitCode: 1 };
|
|
387
|
+
}
|
|
388
|
+
const writeModeRaw = stringFlag(ctx, 'write-mode') ?? 'create-new';
|
|
389
|
+
if (writeModeRaw !== 'create-new' && writeModeRaw !== 'add-version') {
|
|
390
|
+
return {
|
|
391
|
+
kind: 'error',
|
|
392
|
+
stderr: `Invalid --write-mode "${writeModeRaw}". Accepted: create-new, add-version.\n`,
|
|
393
|
+
exitCode: 2,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
try {
|
|
397
|
+
const outcome = await secretsFrom(ctx).set({
|
|
398
|
+
scope: parsed.value.scope,
|
|
399
|
+
envName: parsed.value.envName,
|
|
400
|
+
name,
|
|
401
|
+
value: valueRes.value,
|
|
402
|
+
writeMode: writeModeRaw,
|
|
403
|
+
...(stringFlag(ctx, 'rotation-due-at') !== undefined && {
|
|
404
|
+
rotationDueAt: stringFlag(ctx, 'rotation-due-at'),
|
|
405
|
+
}),
|
|
406
|
+
...(stringFlag(ctx, 'if-version') !== undefined && {
|
|
407
|
+
ifVersion: integerFlag(ctx, 'if-version'),
|
|
408
|
+
}),
|
|
409
|
+
});
|
|
410
|
+
if (outcome.kind === 'version-conflict') {
|
|
411
|
+
return {
|
|
412
|
+
kind: 'error',
|
|
413
|
+
stderr: `Version conflict: ${name} already exists (version ${outcome.currentVersion}). To store a new version, retry with --write-mode=add-version${stringFlag(ctx, 'if-version') !== undefined ? ` --if-version=${outcome.currentVersion}` : ''}.\n`,
|
|
414
|
+
exitCode: 1,
|
|
415
|
+
};
|
|
416
|
+
}
|
|
417
|
+
const summary = {
|
|
418
|
+
name,
|
|
419
|
+
scope: parsed.value.scope,
|
|
420
|
+
envName: parsed.value.envName,
|
|
421
|
+
versionId: outcome.kind === 'ok' ? outcome.versionId : undefined,
|
|
422
|
+
};
|
|
423
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
424
|
+
return {
|
|
425
|
+
kind: 'ok',
|
|
426
|
+
rendered: {
|
|
427
|
+
stdout: rendered.stdout,
|
|
428
|
+
stderr: `\n Set ${name} at ${describeScope(parsed.value.scope)} in ${parsed.value.envName}.\n\n`,
|
|
429
|
+
},
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
catch (err) {
|
|
433
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets set');
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
};
|
|
437
|
+
// ---------------------------------------------------------------------
|
|
438
|
+
// rotate
|
|
439
|
+
// ---------------------------------------------------------------------
|
|
440
|
+
const rotateCmd = {
|
|
441
|
+
kind: 'leaf',
|
|
442
|
+
name: 'rotate',
|
|
443
|
+
description: 'Rotate a secret. Async providers polled to terminal; use --no-wait to bypass polling and return wire response.',
|
|
444
|
+
usage: 'kindgi secrets rotate <NAME> --env=<name> --scope=<kind>[:id] [--new-value | --from-stdin | --from-file <path>] [--revoke-old-after=<ms>] [--no-wait]',
|
|
445
|
+
optionSpec: {
|
|
446
|
+
...SCOPE_OPTION_SPEC,
|
|
447
|
+
'from-stdin': {
|
|
448
|
+
type: 'boolean',
|
|
449
|
+
description: 'Read the new value from stdin.',
|
|
450
|
+
},
|
|
451
|
+
'from-file': {
|
|
452
|
+
type: 'string',
|
|
453
|
+
description: 'Read the new value from this file. A file group or others can access is refused (`chmod 600`).',
|
|
454
|
+
},
|
|
455
|
+
'new-value': {
|
|
456
|
+
type: 'boolean',
|
|
457
|
+
description: 'Prompt for the new value (no echo). Without it, `--from-stdin` or `--from-file`, the secret store must rotate the value itself.',
|
|
458
|
+
},
|
|
459
|
+
'revoke-old-after': {
|
|
460
|
+
type: 'string',
|
|
461
|
+
description: 'Revoke the old version this many milliseconds after the rotation (`0`: at once). Without it, the old version stays valid.',
|
|
462
|
+
},
|
|
463
|
+
'no-wait': {
|
|
464
|
+
type: 'boolean',
|
|
465
|
+
description: "Print the API's first response instead of waiting for an asynchronous rotation to finish.",
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
run: async (ctx) => {
|
|
469
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
470
|
+
if (parsed.kind === 'err')
|
|
471
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
472
|
+
let name;
|
|
473
|
+
try {
|
|
474
|
+
name = requiredPositional(ctx, 0, 'NAME');
|
|
475
|
+
}
|
|
476
|
+
catch (err) {
|
|
477
|
+
return { kind: 'error', stderr: `${err.message}\n`, exitCode: 2 };
|
|
478
|
+
}
|
|
479
|
+
let newValue;
|
|
480
|
+
if (ctx.options['new-value'] === true ||
|
|
481
|
+
ctx.options['from-stdin'] === true ||
|
|
482
|
+
stringFlag(ctx, 'from-file') !== undefined) {
|
|
483
|
+
const seam = valueInputSeamFromCtx(ctx);
|
|
484
|
+
const valueRes = await readValueInput({
|
|
485
|
+
fromStdin: ctx.options['from-stdin'] === true,
|
|
486
|
+
fromFile: stringFlag(ctx, 'from-file'),
|
|
487
|
+
...seam,
|
|
488
|
+
});
|
|
489
|
+
if (valueRes.kind === 'err') {
|
|
490
|
+
return { kind: 'error', stderr: valueRes.stderr, exitCode: 1 };
|
|
491
|
+
}
|
|
492
|
+
newValue = valueRes.value;
|
|
493
|
+
}
|
|
494
|
+
const revokeOldRaw = stringFlag(ctx, 'revoke-old-after');
|
|
495
|
+
const revokeOldAfterMs = revokeOldRaw !== undefined ? Number(revokeOldRaw) : undefined;
|
|
496
|
+
if (revokeOldAfterMs !== undefined && !Number.isFinite(revokeOldAfterMs)) {
|
|
497
|
+
return {
|
|
498
|
+
kind: 'error',
|
|
499
|
+
stderr: `Invalid --revoke-old-after "${revokeOldRaw}": must be a number of milliseconds.\n`,
|
|
500
|
+
exitCode: 2,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
try {
|
|
504
|
+
const outcome = await secretsFrom(ctx).rotate({
|
|
505
|
+
scope: parsed.value.scope,
|
|
506
|
+
envName: parsed.value.envName,
|
|
507
|
+
name,
|
|
508
|
+
...(newValue !== undefined && { newValue }),
|
|
509
|
+
...(revokeOldAfterMs !== undefined && { revokeOldAfterMs }),
|
|
510
|
+
mode: ctx.options['no-wait'] === true ? 'raw' : 'wait-for-complete',
|
|
511
|
+
});
|
|
512
|
+
return renderRotationOutcome(ctx, name, parsed.value, outcome);
|
|
513
|
+
}
|
|
514
|
+
catch (err) {
|
|
515
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets rotate');
|
|
516
|
+
}
|
|
517
|
+
},
|
|
518
|
+
};
|
|
519
|
+
function renderRotationOutcome(ctx, name, target, outcome) {
|
|
520
|
+
if (outcome.kind === 'ok') {
|
|
521
|
+
const summary = {
|
|
522
|
+
name,
|
|
523
|
+
scope: target.scope,
|
|
524
|
+
envName: target.envName,
|
|
525
|
+
newVersionId: outcome.newVersionId,
|
|
526
|
+
oldVersionId: outcome.oldVersionId,
|
|
527
|
+
};
|
|
528
|
+
return {
|
|
529
|
+
kind: 'ok',
|
|
530
|
+
rendered: {
|
|
531
|
+
stdout: renderJson(summary, ctx.globals.format).stdout,
|
|
532
|
+
stderr: `\n Rotated ${name} → v${outcome.newVersionId} (was v${outcome.oldVersionId}).\n\n`,
|
|
533
|
+
},
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
if (outcome.kind === 'raw') {
|
|
537
|
+
return {
|
|
538
|
+
kind: 'ok',
|
|
539
|
+
rendered: renderJson({ raw: outcome.response }, ctx.globals.format),
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
if (outcome.kind === 'timeout') {
|
|
543
|
+
return {
|
|
544
|
+
kind: 'error',
|
|
545
|
+
stderr: `rotation still in progress; check status: kindgi secrets rotations get ${outcome.rotationId}\n`,
|
|
546
|
+
exitCode: 1,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
if (outcome.kind === 'failed') {
|
|
550
|
+
return {
|
|
551
|
+
kind: 'error',
|
|
552
|
+
stderr: `Rotation failed: ${outcome.error}\n`,
|
|
553
|
+
exitCode: 1,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
return {
|
|
557
|
+
kind: 'error',
|
|
558
|
+
stderr: 'Rotation cancelled by the provider.\n',
|
|
559
|
+
exitCode: 1,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
// ---------------------------------------------------------------------
|
|
563
|
+
// revoke
|
|
564
|
+
// ---------------------------------------------------------------------
|
|
565
|
+
const revokeCmd = {
|
|
566
|
+
kind: 'leaf',
|
|
567
|
+
name: 'revoke',
|
|
568
|
+
description: 'Revoke a secret. Default: soft (keeps audit tombstone). --hard: cryptographic erasure.',
|
|
569
|
+
usage: 'kindgi secrets revoke <NAME> --env=<name> --scope=<kind>[:id] [--hard] [--reason=<r>]',
|
|
570
|
+
optionSpec: {
|
|
571
|
+
...SCOPE_OPTION_SPEC,
|
|
572
|
+
hard: {
|
|
573
|
+
type: 'boolean',
|
|
574
|
+
description: 'Erase the value for good instead of keeping an audit tombstone. Cannot be undone.',
|
|
575
|
+
},
|
|
576
|
+
reason: {
|
|
577
|
+
type: 'string',
|
|
578
|
+
description: 'Why the secret is revoked; kept on the record as `revokeReason`.',
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
run: async (ctx) => {
|
|
582
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
583
|
+
if (parsed.kind === 'err')
|
|
584
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
585
|
+
let name;
|
|
586
|
+
try {
|
|
587
|
+
name = requiredPositional(ctx, 0, 'NAME');
|
|
588
|
+
}
|
|
589
|
+
catch (err) {
|
|
590
|
+
return { kind: 'error', stderr: `${err.message}\n`, exitCode: 2 };
|
|
591
|
+
}
|
|
592
|
+
try {
|
|
593
|
+
const out = await secretsFrom(ctx).revoke({
|
|
594
|
+
scope: parsed.value.scope,
|
|
595
|
+
envName: parsed.value.envName,
|
|
596
|
+
name,
|
|
597
|
+
...(ctx.options.hard === true && { hard: true }),
|
|
598
|
+
...(stringFlag(ctx, 'reason') !== undefined && { reason: stringFlag(ctx, 'reason') }),
|
|
599
|
+
});
|
|
600
|
+
const summary = {
|
|
601
|
+
name,
|
|
602
|
+
scope: parsed.value.scope,
|
|
603
|
+
envName: parsed.value.envName,
|
|
604
|
+
revoked: out.revoked,
|
|
605
|
+
hard: out.hard,
|
|
606
|
+
};
|
|
607
|
+
return {
|
|
608
|
+
kind: 'ok',
|
|
609
|
+
rendered: {
|
|
610
|
+
stdout: renderJson(summary, ctx.globals.format).stdout,
|
|
611
|
+
stderr: `\n ${out.hard ? 'HARD-revoked' : 'Revoked'} ${name} at ${describeScope(parsed.value.scope)} in ${parsed.value.envName}.\n\n`,
|
|
612
|
+
},
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
catch (err) {
|
|
616
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets revoke');
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
};
|
|
620
|
+
// ---------------------------------------------------------------------
|
|
621
|
+
// pull — writes metadata manifest to .secrets/<envName>/manifest.json
|
|
622
|
+
// ---------------------------------------------------------------------
|
|
623
|
+
const pullCmd = {
|
|
624
|
+
kind: 'leaf',
|
|
625
|
+
name: 'pull',
|
|
626
|
+
description: 'Fetch secret metadata into .secrets/<envName>/manifest.json. Never fetches bytes.',
|
|
627
|
+
usage: 'kindgi secrets pull --env=<name> --scope=<kind>[:id] [--path <dir>]',
|
|
628
|
+
optionSpec: {
|
|
629
|
+
...SCOPE_OPTION_SPEC,
|
|
630
|
+
path: {
|
|
631
|
+
type: 'string',
|
|
632
|
+
description: 'The pack directory to write `.secrets/<env>/manifest.json` under (default: the current directory).',
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
run: async (ctx) => {
|
|
636
|
+
const parsed = parseScopeAndEnv(ctx);
|
|
637
|
+
if (parsed.kind === 'err')
|
|
638
|
+
return { kind: 'error', stderr: parsed.stderr, exitCode: 2 };
|
|
639
|
+
const runners = pickEnvRunners(ctx);
|
|
640
|
+
if (runners === null) {
|
|
641
|
+
return {
|
|
642
|
+
kind: 'error',
|
|
643
|
+
stderr: 'Internal error: kindgi secrets pull requires env runners for the manifest write. ' +
|
|
644
|
+
'Rebuild the CLI so the production wiring at packages/cli/src/env/defaults.ts is picked up.\n',
|
|
645
|
+
exitCode: 1,
|
|
646
|
+
};
|
|
647
|
+
}
|
|
648
|
+
const packDir = resolvePackDir(ctx);
|
|
649
|
+
const manifestDir = `${packDir}/.secrets/${parsed.value.envName}`;
|
|
650
|
+
const manifestPath = `${manifestDir}/manifest.json`;
|
|
651
|
+
try {
|
|
652
|
+
const records = [];
|
|
653
|
+
let cursor;
|
|
654
|
+
// Cursor loop: pull all pages.
|
|
655
|
+
for (let i = 0; i < 1_000; i += 1) {
|
|
656
|
+
const page = await secretsFrom(ctx).list({
|
|
657
|
+
scope: parsed.value.scope,
|
|
658
|
+
envName: parsed.value.envName,
|
|
659
|
+
...(cursor !== undefined && { cursor: cursor }),
|
|
660
|
+
});
|
|
661
|
+
for (const r of page.data)
|
|
662
|
+
records.push(r);
|
|
663
|
+
if (page.nextCursor === undefined)
|
|
664
|
+
break;
|
|
665
|
+
cursor = page.nextCursor;
|
|
666
|
+
}
|
|
667
|
+
const manifest = {
|
|
668
|
+
generatedAt: new Date().toISOString(),
|
|
669
|
+
scope: parsed.value.scope,
|
|
670
|
+
envName: parsed.value.envName,
|
|
671
|
+
// Metadata only — value + tag payloads never cross into the manifest.
|
|
672
|
+
secrets: records.map((r) => ({
|
|
673
|
+
name: r.name,
|
|
674
|
+
currentVersion: r.currentVersion,
|
|
675
|
+
createdAt: r.createdAt,
|
|
676
|
+
updatedAt: r.updatedAt,
|
|
677
|
+
...(r.revokedAt !== undefined && { revokedAt: r.revokedAt }),
|
|
678
|
+
...(r.rotationDueAt !== undefined && { rotationDueAt: r.rotationDueAt }),
|
|
679
|
+
})),
|
|
680
|
+
};
|
|
681
|
+
await runners.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`);
|
|
682
|
+
const summary = {
|
|
683
|
+
manifestPath,
|
|
684
|
+
count: records.length,
|
|
685
|
+
envName: parsed.value.envName,
|
|
686
|
+
};
|
|
687
|
+
return {
|
|
688
|
+
kind: 'ok',
|
|
689
|
+
rendered: {
|
|
690
|
+
stdout: renderJson(summary, ctx.globals.format).stdout,
|
|
691
|
+
stderr: `\n Wrote ${records.length} secret metadata entries → ${manifestPath}\n (values NEVER cross the wire on pull.)\n\n`,
|
|
692
|
+
},
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
catch (err) {
|
|
696
|
+
return commandResultFromThrown(err, ctx, 'kindgi secrets pull');
|
|
697
|
+
}
|
|
698
|
+
},
|
|
699
|
+
};
|
|
700
|
+
function pickEnvRunners(ctx) {
|
|
701
|
+
if (ctx.envRunners !== undefined)
|
|
702
|
+
return ctx.envRunners;
|
|
703
|
+
return null;
|
|
704
|
+
}
|
|
705
|
+
function resolvePackDir(ctx) {
|
|
706
|
+
const raw = ctx.options.path;
|
|
707
|
+
if (typeof raw === 'string' && raw !== '')
|
|
708
|
+
return raw;
|
|
709
|
+
return ctx.cwd;
|
|
710
|
+
}
|
|
711
|
+
// ---------------------------------------------------------------------
|
|
712
|
+
// Group registration
|
|
713
|
+
// ---------------------------------------------------------------------
|
|
714
|
+
export const secretsCommand = {
|
|
715
|
+
kind: 'group',
|
|
716
|
+
name: 'secrets',
|
|
717
|
+
description: 'Manage per-environment secrets via the /v1/secrets/* wire.',
|
|
718
|
+
subcommands: [listCmd, getCmd, setCmd, rotateCmd, revokeCmd, pullCmd],
|
|
719
|
+
};
|
|
720
|
+
// ---------------------------------------------------------------------
|
|
721
|
+
// Helpers
|
|
722
|
+
// ---------------------------------------------------------------------
|
|
723
|
+
function describeScope(scope) {
|
|
724
|
+
if (scope.kind === 'tenant')
|
|
725
|
+
return 'tenant';
|
|
726
|
+
if (scope.kind === 'org')
|
|
727
|
+
return `org:${scope.orgId}`;
|
|
728
|
+
return `project:${scope.projectId}`;
|
|
729
|
+
}
|
|
730
|
+
function sanitizeSecret(rec) {
|
|
731
|
+
// Structural: no `value` on SecretRecord. Kept as an explicit filter
|
|
732
|
+
// so the CLI's output surface stays audit-obvious.
|
|
733
|
+
return {
|
|
734
|
+
name: rec.name,
|
|
735
|
+
scope: rec.scope,
|
|
736
|
+
envName: rec.envName,
|
|
737
|
+
currentVersion: rec.currentVersion,
|
|
738
|
+
createdAt: rec.createdAt,
|
|
739
|
+
updatedAt: rec.updatedAt,
|
|
740
|
+
...(rec.revokedAt !== undefined && { revokedAt: rec.revokedAt }),
|
|
741
|
+
...(rec.revokeReason !== undefined && { revokeReason: rec.revokeReason }),
|
|
742
|
+
...(rec.rotationDueAt !== undefined && { rotationDueAt: rec.rotationDueAt }),
|
|
743
|
+
...(rec.tags !== undefined && { tags: rec.tags }),
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
// Suppress unused import — `SecretVersionRecord` is only referenced in
|
|
747
|
+
// type comments today, but the shape is anticipated for `versions list`
|
|
748
|
+
// / `versions get` sub-verbs the docs call out.
|
|
749
|
+
void undefined;
|
|
750
|
+
//# sourceMappingURL=secrets.js.map
|