@kindgi/cli 0.0.0-bootstrap.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +738 -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 +48 -0
- package/dist/build/bundle.d.ts.map +1 -0
- package/dist/build/bundle.js +125 -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 +197 -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 +597 -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 +100 -0
- package/dist/build/host-install.d.ts.map +1 -0
- package/dist/build/host-install.js +443 -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 +25 -0
- package/dist/commands/auth.d.ts.map +1 -0
- package/dist/commands/auth.js +283 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/build.d.ts +46 -0
- package/dist/commands/build.d.ts.map +1 -0
- package/dist/commands/build.js +902 -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 +43 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +1110 -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 +48 -0
- package/dist/commands/helpers.d.ts.map +1 -0
- package/dist/commands/helpers.js +110 -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 +454 -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 +212 -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 +223 -0
- package/dist/commands/runs.js.map +1 -0
- package/dist/commands/secrets.d.ts +18 -0
- package/dist/commands/secrets.d.ts.map +1 -0
- package/dist/commands/secrets.js +702 -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 +162 -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 +22 -0
- package/dist/commands/unwired.d.ts.map +1 -0
- package/dist/commands/unwired.js +52 -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 +162 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +45 -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 +10 -0
- package/dist/dev/bundler.d.ts.map +1 -0
- package/dist/dev/bundler.js +235 -0
- package/dist/dev/bundler.js.map +1 -0
- package/dist/dev/defaults.d.ts +140 -0
- package/dist/dev/defaults.d.ts.map +1 -0
- package/dist/dev/defaults.js +709 -0
- package/dist/dev/defaults.js.map +1 -0
- package/dist/dev/dev-only-imports.d.ts +15 -0
- package/dist/dev/dev-only-imports.d.ts.map +1 -0
- package/dist/dev/dev-only-imports.js +57 -0
- package/dist/dev/dev-only-imports.js.map +1 -0
- package/dist/dev/docker-compose.dev.yml +68 -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 +40 -0
- package/dist/dev/pack-service.d.ts.map +1 -0
- package/dist/dev/pack-service.js +189 -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/postgres-container.d.ts +77 -0
- package/dist/dev/postgres-container.d.ts.map +1 -0
- package/dist/dev/postgres-container.js +346 -0
- package/dist/dev/postgres-container.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 +312 -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 +71 -0
- package/dist/dev/runtime-container.d.ts.map +1 -0
- package/dist/dev/runtime-container.js +173 -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 +26 -0
- package/dist/dev/runtime-image.d.ts.map +1 -0
- package/dist/dev/runtime-image.js +44 -0
- package/dist/dev/runtime-image.js.map +1 -0
- package/dist/dev/runtime-registry.d.ts +73 -0
- package/dist/dev/runtime-registry.d.ts.map +1 -0
- package/dist/dev/runtime-registry.js +111 -0
- package/dist/dev/runtime-registry.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 +111 -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 +86 -0
- package/dist/init/augment-scaffolder.d.ts.map +1 -0
- package/dist/init/augment-scaffolder.js +538 -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/pnpm-workspace-patcher.d.ts +55 -0
- package/dist/init/pnpm-workspace-patcher.d.ts.map +1 -0
- package/dist/init/pnpm-workspace-patcher.js +248 -0
- package/dist/init/pnpm-workspace-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 +18 -0
- package/dist/init/template-files.d.ts.map +1 -0
- package/dist/init/template-files.js +53 -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 +113 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +199 -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 +88 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +100 -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 +252 -0
- package/dist/sdk-skills/kindgi-authoring-flows/SKILL.md +302 -0
- package/dist/sdk-skills/kindgi-authoring-guardrails/SKILL.md +297 -0
- package/dist/sdk-skills/kindgi-authoring-mcp-servers/SKILL.md +289 -0
- package/dist/sdk-skills/kindgi-authoring-providers/SKILL.md +705 -0
- package/dist/sdk-skills/kindgi-authoring-tools/SKILL.md +298 -0
- package/dist/sdk-skills/kindgi-framework-feedback/SKILL.md +211 -0
- package/dist/sdk-skills/kindgi-getting-started/SKILL.md +189 -0
- package/dist/sdk-skills/kindgi-python-authoring-agents/SKILL.md +205 -0
- package/dist/sdk-skills/kindgi-python-authoring-flows/SKILL.md +325 -0
- package/dist/sdk-skills/kindgi-python-authoring-guardrails/SKILL.md +176 -0
- package/dist/sdk-skills/kindgi-python-authoring-tools/SKILL.md +305 -0
- package/dist/sdk-skills/kindgi-python-getting-started/SKILL.md +242 -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/.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/gitignore +8 -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 +8 -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/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/gitignore +9 -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/.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/gitignore +8 -0
- package/dist/templates/sample/guardrails/response-not-empty/index.ts.tmpl +58 -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 +8 -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/terminal-input.d.ts +17 -0
- package/dist/terminal-input.d.ts.map +1 -0
- package/dist/terminal-input.js +88 -0
- package/dist/terminal-input.js.map +1 -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,902 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
/**
|
|
4
|
+
* `kindgi build` — client-side build orchestrator producing a signed
|
|
5
|
+
* `deploy-envelope.json` for `kindgi deploy` to POST.
|
|
6
|
+
*
|
|
7
|
+
* Steps:
|
|
8
|
+
*
|
|
9
|
+
* 1. Resolve config + target env (endpoint, signingKey, tenantId).
|
|
10
|
+
* 2. Local indexer pass → `expected-index.json`.
|
|
11
|
+
* 3. esbuild bundle → `dist/`.
|
|
12
|
+
* 4. Generate multi-stage Containerfile.
|
|
13
|
+
* 5. Tar the bundle → `pack.tgz`.
|
|
14
|
+
* 6. sha256(pack.tgz).
|
|
15
|
+
* 7. POST /v1/build (multipart) → job id + SSE URL.
|
|
16
|
+
* 8. Stream build logs; capture terminal payload.
|
|
17
|
+
* 9. Local integrity gate (hash + optional docker-pull-and-diff).
|
|
18
|
+
* 10. Sign the canonicalised envelope body locally.
|
|
19
|
+
* 11. Write `deploy-envelope.json`.
|
|
20
|
+
* 12. Print summary.
|
|
21
|
+
*
|
|
22
|
+
* Every side effect flows through `ctx.buildRunners` so tests substitute
|
|
23
|
+
* fixtures. `POST /v1/deployments` is NOT called from this command —
|
|
24
|
+
* that's `kindgi deploy`.
|
|
25
|
+
*/
|
|
26
|
+
import { mkdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
27
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
28
|
+
import { findKindgiConfig, resolveDiscovery, } from '@kindgi/handler-runtime';
|
|
29
|
+
import { checkAptPackages } from '../build/apt.js';
|
|
30
|
+
import { PACK_SERVICE_COMMAND } from '../build/containerfile.js';
|
|
31
|
+
import { collectIncludeFiles, readBundleConfig } from '../build/context-files.js';
|
|
32
|
+
import { buildEnvelope, canonicaliseSignatureBody, sha256Hex } from '../build/envelope.js';
|
|
33
|
+
import { devOnlyImports, resolveHostInstall, } from '../build/host-install.js';
|
|
34
|
+
import { readImageConfig } from '../build/image-config.js';
|
|
35
|
+
import { checkIntegrity } from '../build/integrity.js';
|
|
36
|
+
import { nodeBaseImageFor } from '../build/node-image.js';
|
|
37
|
+
import { resolvePackRoots } from '../build/pack-root.js';
|
|
38
|
+
import { DEFAULT_PYTHON_BASE_IMAGE_REF, DEFAULT_UV_IMAGE_REF, PYTHON_LOCKFILES, collectPythonContextFiles, } from '../build/python-image.js';
|
|
39
|
+
import { resolvePackPython } from '../dev/pack-code.js';
|
|
40
|
+
import { renderJson } from '../output.js';
|
|
41
|
+
import { loadPackConfig } from '../pack-config.js';
|
|
42
|
+
export const DEFAULT_OUT_DIR = '.kindgi/build';
|
|
43
|
+
/** SOURCE_DATE_EPOCH=0 baseline — the build-server also pins epoch=0. */
|
|
44
|
+
export const DEFAULT_PUBLISHED_AT = '1970-01-01T00:00:00.000Z';
|
|
45
|
+
export const buildCommand = {
|
|
46
|
+
kind: 'leaf',
|
|
47
|
+
name: 'build',
|
|
48
|
+
description: 'Bundle + build + sign a pack image. Emits deploy-envelope.json for `kindgi deploy`. ' +
|
|
49
|
+
"`--local` builds the image with this machine's Docker instead: no build service, no signing; " +
|
|
50
|
+
'with `--push`, it pushes the image, signs it, and writes the envelope.',
|
|
51
|
+
usage: 'kindgi build [--local [--push [<repository>]] [--platform <os/arch>]] [--target <t>] [--endpoint <url>] [--env <name>] [--out <dir>] ' +
|
|
52
|
+
'[--artifact-version <v>] [--published-at <iso>] [--tenant <id>] [--signing-key <path>] ' +
|
|
53
|
+
'[--registry-push-creds <ref>] [--skip-integrity-gate] [--skip-image-pull] [--skip-sign] [--path <dir>]',
|
|
54
|
+
optionSpec: {
|
|
55
|
+
local: {
|
|
56
|
+
type: 'boolean',
|
|
57
|
+
description: "Build with this machine's Docker instead of a build server: no signing and no envelope unless `--push`. TypeScript packs only.",
|
|
58
|
+
},
|
|
59
|
+
push: {
|
|
60
|
+
type: 'string',
|
|
61
|
+
optionalValue: true,
|
|
62
|
+
description: "With `--local`: push the image, sign it and write the envelope. Default repository: the env block's `registry` + `/<packId>`.",
|
|
63
|
+
},
|
|
64
|
+
platform: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: "With `--local`: the image's platform. Default: `linux/amd64` when pushing, else this machine's.",
|
|
67
|
+
},
|
|
68
|
+
target: {
|
|
69
|
+
type: 'string',
|
|
70
|
+
description: "The build target, set in the image build and sent to the build server. Default: the env block's `buildTarget`, else the env name.",
|
|
71
|
+
},
|
|
72
|
+
endpoint: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description: "The build server. Default: the env block's `build`. Not used with `--local`.",
|
|
75
|
+
},
|
|
76
|
+
env: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: 'The environment block in `kindgi.config.ts` to build for. Default: `staging`.',
|
|
79
|
+
},
|
|
80
|
+
out: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
description: 'Where the build output and `deploy-envelope.json` go. Default: `.kindgi/build` under the pack root.',
|
|
83
|
+
},
|
|
84
|
+
'artifact-version': {
|
|
85
|
+
type: 'string',
|
|
86
|
+
description: "The artifact version, in the image and its signature. Default: today's date as `YYYYMMDD.1` (UTC).",
|
|
87
|
+
},
|
|
88
|
+
'published-at': {
|
|
89
|
+
type: 'string',
|
|
90
|
+
description: 'The publish time (ISO 8601), in the image and its signature. Default: the Unix epoch, so builds are reproducible.',
|
|
91
|
+
},
|
|
92
|
+
tenant: {
|
|
93
|
+
type: 'string',
|
|
94
|
+
description: "The tenant the signature names. Default: the env block's `tenantId`, else `KINDGI_TENANT_ID`.",
|
|
95
|
+
},
|
|
96
|
+
'signing-key': {
|
|
97
|
+
type: 'string',
|
|
98
|
+
description: "The Ed25519 private key (PEM) to sign with. Default: the env block's `signingKey`.",
|
|
99
|
+
},
|
|
100
|
+
'signer-key-id': {
|
|
101
|
+
type: 'string',
|
|
102
|
+
description: "The key id the signature names. Default: the env block's `signerKeyId`, else the key file's name.",
|
|
103
|
+
},
|
|
104
|
+
'registry-push-creds': {
|
|
105
|
+
type: 'string',
|
|
106
|
+
description: 'A reference to the registry push credentials for the build server to use, passed through as is.',
|
|
107
|
+
},
|
|
108
|
+
'skip-integrity-gate': {
|
|
109
|
+
type: 'boolean',
|
|
110
|
+
description: "Sign without checking the image's index against the local one. Prints a warning. Build-server builds only.",
|
|
111
|
+
},
|
|
112
|
+
'skip-image-pull': {
|
|
113
|
+
type: 'boolean',
|
|
114
|
+
description: "Check the image's index by hash only, without pulling the image. Build-server builds only.",
|
|
115
|
+
},
|
|
116
|
+
'skip-sign': {
|
|
117
|
+
type: 'boolean',
|
|
118
|
+
description: 'Write an unsigned envelope (for CI that signs elsewhere); `kindgi deploy` refuses it.',
|
|
119
|
+
},
|
|
120
|
+
path: { type: 'string', description: 'The pack root. Default: the current directory.' },
|
|
121
|
+
},
|
|
122
|
+
run: async (ctx) => runBuild(ctx),
|
|
123
|
+
};
|
|
124
|
+
export async function runBuild(ctx) {
|
|
125
|
+
const runners = ctx.buildRunners;
|
|
126
|
+
if (runners === undefined) {
|
|
127
|
+
return {
|
|
128
|
+
kind: 'error',
|
|
129
|
+
stderr: 'Internal error: kindgi build requires build runners to be wired. ' +
|
|
130
|
+
'Rebuild the CLI (`pnpm --filter @kindgi/cli build`).\n',
|
|
131
|
+
exitCode: 1,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
const parsed = await resolveBuildArgs(ctx);
|
|
135
|
+
if (parsed.kind === 'error')
|
|
136
|
+
return parsed;
|
|
137
|
+
const args = parsed.args;
|
|
138
|
+
if (args.local)
|
|
139
|
+
return runLocalBuild(ctx, runners, args);
|
|
140
|
+
const banner = [];
|
|
141
|
+
const lines = (s) => {
|
|
142
|
+
banner.push(s);
|
|
143
|
+
};
|
|
144
|
+
lines('');
|
|
145
|
+
lines(` Environment: ${args.envName}`);
|
|
146
|
+
lines(` Build: ${args.endpoint}`);
|
|
147
|
+
lines(` Target: ${args.buildTarget}`);
|
|
148
|
+
lines(` Tenant: ${args.tenantId}`);
|
|
149
|
+
lines(` Signing key: ${args.signingKeyPath ?? '(--skip-sign)'}`);
|
|
150
|
+
lines(` Output: ${args.outDir}`);
|
|
151
|
+
lines('');
|
|
152
|
+
// ---- 1. Local indexer pass ------------------------------------------
|
|
153
|
+
const expectedIndexPath = join(args.outDir, 'expected-index.json');
|
|
154
|
+
await mkdir(args.outDir, { recursive: true });
|
|
155
|
+
const prepared = args.language === 'python'
|
|
156
|
+
? await preparePythonContext(ctx, runners, args, expectedIndexPath, lines)
|
|
157
|
+
: await prepareNodeTarball(runners, args, expectedIndexPath, lines);
|
|
158
|
+
if (prepared.kind === 'error')
|
|
159
|
+
return prepared;
|
|
160
|
+
const { tarball, tarballHash, counts } = prepared;
|
|
161
|
+
lines('');
|
|
162
|
+
// ---- 6. POST /v1/build + SSE stream ---------------------------------
|
|
163
|
+
lines(' Uploading to build service');
|
|
164
|
+
const posted = await runners.postBuild({
|
|
165
|
+
endpoint: args.endpoint,
|
|
166
|
+
tarballBytes: tarball.bytes,
|
|
167
|
+
tarballSha256: tarballHash,
|
|
168
|
+
buildTarget: args.buildTarget,
|
|
169
|
+
...(args.registryPushCredsRef !== undefined && {
|
|
170
|
+
registryPushCredsRef: args.registryPushCredsRef,
|
|
171
|
+
}),
|
|
172
|
+
fetchImpl: ctx.fetch,
|
|
173
|
+
});
|
|
174
|
+
lines(` ✓ POST /v1/build → buildJob: ${posted.buildJobId}`);
|
|
175
|
+
let terminal;
|
|
176
|
+
if (posted.terminal !== undefined) {
|
|
177
|
+
// Idempotent replay — server returned the terminal payload inline.
|
|
178
|
+
terminal = posted.terminal;
|
|
179
|
+
lines(' ✓ Existing build (idempotent replay)');
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
lines(' → Streaming build logs');
|
|
183
|
+
const captured = [];
|
|
184
|
+
terminal = await runners.streamBuildLogs({
|
|
185
|
+
endpoint: args.endpoint,
|
|
186
|
+
buildJobId: posted.buildJobId,
|
|
187
|
+
fetchImpl: ctx.fetch,
|
|
188
|
+
onLog: (line) => {
|
|
189
|
+
captured.push(line);
|
|
190
|
+
// Fold multi-line output into the banner one line at a time,
|
|
191
|
+
// so the build log reads as part of the summary.
|
|
192
|
+
lines(` ${line}`);
|
|
193
|
+
},
|
|
194
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
if (terminal.status === 'failed') {
|
|
198
|
+
return failure(`Build failed: [${terminal.error?.code ?? 'unknown'}] ${terminal.error?.message ?? 'no message'}\n`);
|
|
199
|
+
}
|
|
200
|
+
if (terminal.imageRef === undefined ||
|
|
201
|
+
terminal.imageDigest === undefined ||
|
|
202
|
+
terminal.indexJson === undefined ||
|
|
203
|
+
terminal.indexHash === undefined) {
|
|
204
|
+
return failure('Build terminal payload missing required fields (imageRef / imageDigest / indexJson / indexHash).\n');
|
|
205
|
+
}
|
|
206
|
+
lines(` ✓ Build complete (${terminal.imageRef})`);
|
|
207
|
+
lines('');
|
|
208
|
+
// ---- 7. Local integrity gate ----------------------------------------
|
|
209
|
+
const expectedBytes = new Uint8Array(await readFile(expectedIndexPath));
|
|
210
|
+
const expectedHash = await sha256Hex(expectedBytes);
|
|
211
|
+
let serverBytes;
|
|
212
|
+
lines(' Verifying build output');
|
|
213
|
+
if (args.skipIntegrityGate) {
|
|
214
|
+
lines(' ⚠ Integrity gate SKIPPED — --skip-integrity-gate. Signature will attest to');
|
|
215
|
+
lines(' the server-reported hash without local re-verification. NOT RECOMMENDED.');
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
if (args.skipImagePull || runners.pullImageIndex === undefined) {
|
|
219
|
+
lines(' ⚠ Image pull SKIPPED — hash-only comparison. Byte-diff path unavailable');
|
|
220
|
+
lines(' (requires Docker). CI environments where this is expected can safely');
|
|
221
|
+
lines(' omit --skip-image-pull warnings.');
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
try {
|
|
225
|
+
const pulled = await runners.pullImageIndex({
|
|
226
|
+
imageRef: terminal.imageRef,
|
|
227
|
+
workDir: args.outDir,
|
|
228
|
+
onLog: (line) => lines(` ${line}`),
|
|
229
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
230
|
+
});
|
|
231
|
+
serverBytes = pulled.bytes;
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
lines(` ⚠ docker pull failed: ${err.message}`);
|
|
235
|
+
lines(' Falling back to hash-only comparison.');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const check = checkIntegrity({
|
|
239
|
+
expectedBytes,
|
|
240
|
+
expectedHash,
|
|
241
|
+
serverReportedHash: terminal.indexHash,
|
|
242
|
+
...(serverBytes !== undefined && { serverBytes }),
|
|
243
|
+
});
|
|
244
|
+
if (!check.ok) {
|
|
245
|
+
return failure(`Integrity gate FAILED — refusing to sign.\n\n${check.reason ?? 'unknown reason'}\n`);
|
|
246
|
+
}
|
|
247
|
+
lines(` ✓ indexHash matches local indexer output (${terminal.indexHash.slice(0, 14)}…)`);
|
|
248
|
+
if (serverBytes !== undefined) {
|
|
249
|
+
lines(' ✓ /app/index.json extracted from image matches byte-for-byte');
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
lines('');
|
|
253
|
+
// ---- 8. Sign + 9. emit deploy-envelope.json ------------------------------
|
|
254
|
+
const written = await signAndWriteEnvelope(runners, args, lines, {
|
|
255
|
+
imageRef: terminal.imageRef,
|
|
256
|
+
imageDigest: terminal.imageDigest,
|
|
257
|
+
index: terminal.indexJson,
|
|
258
|
+
indexHash: terminal.indexHash,
|
|
259
|
+
...(terminal.buildLogsUrl !== undefined && { buildLogsUrl: terminal.buildLogsUrl }),
|
|
260
|
+
});
|
|
261
|
+
if (written.kind === 'error')
|
|
262
|
+
return written;
|
|
263
|
+
const { envelopePath, signature } = written;
|
|
264
|
+
const summary = {
|
|
265
|
+
envName: args.envName,
|
|
266
|
+
envelopePath,
|
|
267
|
+
buildJobId: posted.buildJobId,
|
|
268
|
+
imageRef: terminal.imageRef,
|
|
269
|
+
imageDigest: terminal.imageDigest,
|
|
270
|
+
artifactVersion: args.artifactVersion,
|
|
271
|
+
publishedAt: args.publishedAt,
|
|
272
|
+
indexHash: terminal.indexHash,
|
|
273
|
+
indexCounts: counts,
|
|
274
|
+
tarballSha256: tarballHash,
|
|
275
|
+
tarballBytes: tarball.size,
|
|
276
|
+
signed: signature !== undefined,
|
|
277
|
+
...(signature !== undefined && { signerKeyId: signature.keyId }),
|
|
278
|
+
integrityGateSkipped: args.skipIntegrityGate,
|
|
279
|
+
imagePullSkipped: args.skipImagePull || runners.pullImageIndex === undefined,
|
|
280
|
+
};
|
|
281
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
282
|
+
return {
|
|
283
|
+
kind: 'ok',
|
|
284
|
+
rendered: { stdout: rendered.stdout, stderr: `${banner.join('\n')}\n` },
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
// ---------------------------------------------------------------------
|
|
288
|
+
// --local: build with this machine's Docker
|
|
289
|
+
// ---------------------------------------------------------------------
|
|
290
|
+
/**
|
|
291
|
+
* `kindgi build --local`: the image built with this machine's Docker
|
|
292
|
+
* (`docker buildx build --load`) into its image store, as
|
|
293
|
+
* `kindgi-pack/<packId>:<artifactVersion>`. The same context and
|
|
294
|
+
* integrity gate as a build-service build: the image's `/app/index.json`
|
|
295
|
+
* must equal the local index byte for byte. No build service, no
|
|
296
|
+
* signature, no deploy envelope: push the image yourself, or run it.
|
|
297
|
+
*/
|
|
298
|
+
async function runLocalBuild(ctx, runners, args) {
|
|
299
|
+
if (runners.dockerBuild === undefined || runners.pullImageIndex === undefined) {
|
|
300
|
+
return failure('kindgi build --local needs Docker runners; rebuild the CLI.\n');
|
|
301
|
+
}
|
|
302
|
+
const banner = [];
|
|
303
|
+
const lines = (s) => {
|
|
304
|
+
banner.push(s);
|
|
305
|
+
};
|
|
306
|
+
const pushTo = args.pushRepository;
|
|
307
|
+
lines('');
|
|
308
|
+
lines(pushTo !== undefined
|
|
309
|
+
? ` Build: local, pushed to ${pushTo}`
|
|
310
|
+
: ' Build: local (docker buildx --load)');
|
|
311
|
+
if (args.platform !== undefined)
|
|
312
|
+
lines(` Platform: ${args.platform}`);
|
|
313
|
+
lines(` Output: ${args.outDir}`);
|
|
314
|
+
const expectedIndexPath = join(args.outDir, 'expected-index.json');
|
|
315
|
+
await mkdir(args.outDir, { recursive: true });
|
|
316
|
+
const context = await prepareNodeContext(runners, args, expectedIndexPath, lines);
|
|
317
|
+
if (context.kind === 'error')
|
|
318
|
+
return withBanner(context, banner);
|
|
319
|
+
const expectedBytes = new Uint8Array(await readFile(expectedIndexPath));
|
|
320
|
+
const packId = JSON.parse(new TextDecoder().decode(expectedBytes))
|
|
321
|
+
.packId;
|
|
322
|
+
const tag = `${pushTo ?? `kindgi-pack/${typeof packId === 'string' ? packId : 'pack'}`}:${args.artifactVersion}`;
|
|
323
|
+
lines('');
|
|
324
|
+
lines(` Building ${tag}`);
|
|
325
|
+
const built = await runners.dockerBuild({
|
|
326
|
+
contextDir: context.contextDir,
|
|
327
|
+
tag,
|
|
328
|
+
buildTarget: args.buildTarget,
|
|
329
|
+
...(args.platform !== undefined && { platform: args.platform }),
|
|
330
|
+
...(pushTo !== undefined && { push: true }),
|
|
331
|
+
secrets: context.install.secrets.map((s) => ({
|
|
332
|
+
id: s.id,
|
|
333
|
+
src: join(context.install.root, s.file),
|
|
334
|
+
})),
|
|
335
|
+
onLog: (line) => lines(` ${line}`),
|
|
336
|
+
});
|
|
337
|
+
if (built.kind === 'err')
|
|
338
|
+
return withBanner(failure(`Image build failed: ${built.message}\n`), banner);
|
|
339
|
+
if (pushTo !== undefined && built.digest === undefined) {
|
|
340
|
+
return withBanner(failure(`The push of ${tag} reported no digest.\n`), banner);
|
|
341
|
+
}
|
|
342
|
+
// Pushed: the image is the registry's, by digest; loaded: the local tag.
|
|
343
|
+
const imageRef = pushTo !== undefined ? `${pushTo}@${built.digest}` : tag;
|
|
344
|
+
lines(pushTo !== undefined
|
|
345
|
+
? ` ✓ Pushed ${imageRef}`
|
|
346
|
+
: ` ✓ Built ${tag} (${built.imageId.slice(0, 19)}…)`);
|
|
347
|
+
lines('');
|
|
348
|
+
lines(' Verifying build output');
|
|
349
|
+
const pulled = await runners.pullImageIndex({
|
|
350
|
+
imageRef,
|
|
351
|
+
workDir: args.outDir,
|
|
352
|
+
pull: pushTo !== undefined,
|
|
353
|
+
...(args.platform !== undefined && { platform: args.platform }),
|
|
354
|
+
...(ctx.stopSignal !== undefined && { signal: ctx.stopSignal }),
|
|
355
|
+
});
|
|
356
|
+
const expectedHash = await sha256Hex(expectedBytes);
|
|
357
|
+
const check = checkIntegrity({
|
|
358
|
+
expectedBytes,
|
|
359
|
+
expectedHash,
|
|
360
|
+
serverReportedHash: await sha256Hex(pulled.bytes),
|
|
361
|
+
serverBytes: pulled.bytes,
|
|
362
|
+
});
|
|
363
|
+
if (!check.ok) {
|
|
364
|
+
return withBanner(failure(`Integrity gate FAILED: the image's index isn't the local one.\n\n${check.reason ?? ''}\n`), banner);
|
|
365
|
+
}
|
|
366
|
+
lines(' ✓ /app/index.json in the image matches the local index byte for byte');
|
|
367
|
+
lines('');
|
|
368
|
+
if (pushTo !== undefined && built.digest !== undefined) {
|
|
369
|
+
const written = await signAndWriteEnvelope(runners, args, lines, {
|
|
370
|
+
imageRef,
|
|
371
|
+
imageDigest: built.digest,
|
|
372
|
+
index: JSON.parse(new TextDecoder().decode(expectedBytes)),
|
|
373
|
+
indexHash: expectedHash,
|
|
374
|
+
});
|
|
375
|
+
if (written.kind === 'error')
|
|
376
|
+
return withBanner(written, banner);
|
|
377
|
+
const rendered = renderJson({
|
|
378
|
+
envName: args.envName,
|
|
379
|
+
envelopePath: written.envelopePath,
|
|
380
|
+
imageRef,
|
|
381
|
+
imageDigest: built.digest,
|
|
382
|
+
platform: args.platform,
|
|
383
|
+
artifactVersion: args.artifactVersion,
|
|
384
|
+
publishedAt: args.publishedAt,
|
|
385
|
+
indexHash: expectedHash,
|
|
386
|
+
indexCounts: context.counts,
|
|
387
|
+
signed: written.signature !== undefined,
|
|
388
|
+
...(written.signature !== undefined && { signerKeyId: written.signature.keyId }),
|
|
389
|
+
local: true,
|
|
390
|
+
}, ctx.globals.format);
|
|
391
|
+
return { kind: 'ok', rendered: { stdout: rendered.stdout, stderr: `${banner.join('\n')}\n` } };
|
|
392
|
+
}
|
|
393
|
+
lines(' Run it:');
|
|
394
|
+
lines(` docker run --rm -p 8080:8080 -e KINDGI_PACK_SERVICE_TOKEN=<token> --env-file <the pack's env> ${tag}`);
|
|
395
|
+
lines(` (the pack service: ${PACK_SERVICE_COMMAND.join(' ')})`);
|
|
396
|
+
lines('');
|
|
397
|
+
const rendered = renderJson({
|
|
398
|
+
imageRef: tag,
|
|
399
|
+
imageId: built.imageId,
|
|
400
|
+
artifactVersion: args.artifactVersion,
|
|
401
|
+
publishedAt: args.publishedAt,
|
|
402
|
+
indexHash: expectedHash,
|
|
403
|
+
indexCounts: context.counts,
|
|
404
|
+
local: true,
|
|
405
|
+
}, ctx.globals.format);
|
|
406
|
+
return { kind: 'ok', rendered: { stdout: rendered.stdout, stderr: `${banner.join('\n')}\n` } };
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* Sign the image (Ed25519 over imageDigest, artifactVersion, indexHash,
|
|
410
|
+
* tenantId, publishedAt) and write `deploy-envelope.json` for
|
|
411
|
+
* `kindgi deploy`: the build service's path and `--local --push` alike.
|
|
412
|
+
*/
|
|
413
|
+
async function signAndWriteEnvelope(runners, args, lines, image) {
|
|
414
|
+
let signature;
|
|
415
|
+
if (args.skipSign) {
|
|
416
|
+
lines(' ⚠ Signing SKIPPED — --skip-sign. Emitted envelope is unsigned; deploy will refuse');
|
|
417
|
+
lines(' it. Use only for CI that signs elsewhere.');
|
|
418
|
+
}
|
|
419
|
+
else if (args.signingKeyPath === undefined) {
|
|
420
|
+
return failure('Signing key not configured. Set environments.<env>.signingKey in kindgi.config.ts, ' +
|
|
421
|
+
'pass --signing-key <path>, or use --skip-sign to emit an unsigned envelope.\n');
|
|
422
|
+
}
|
|
423
|
+
else {
|
|
424
|
+
const privateKeyPem = await readFile(args.signingKeyPath, 'utf8');
|
|
425
|
+
const message = canonicaliseSignatureBody({
|
|
426
|
+
imageDigest: image.imageDigest,
|
|
427
|
+
artifactVersion: args.artifactVersion,
|
|
428
|
+
indexHash: image.indexHash,
|
|
429
|
+
tenantId: args.tenantId,
|
|
430
|
+
publishedAt: args.publishedAt,
|
|
431
|
+
});
|
|
432
|
+
const signed = await runners.signEnvelope({ privateKeyPem, message });
|
|
433
|
+
signature = { ...signed, keyId: args.signerKeyId };
|
|
434
|
+
lines(' Signing locally');
|
|
435
|
+
lines(' ✓ Ed25519 signature over (imageDigest, artifactVersion, indexHash, tenantId, publishedAt)');
|
|
436
|
+
lines(` key: ${args.signerKeyId}`);
|
|
437
|
+
}
|
|
438
|
+
const envelope = buildEnvelope({
|
|
439
|
+
imageRef: image.imageRef,
|
|
440
|
+
imageDigest: image.imageDigest,
|
|
441
|
+
artifactVersion: args.artifactVersion,
|
|
442
|
+
index: image.index,
|
|
443
|
+
indexHash: image.indexHash,
|
|
444
|
+
tenantId: args.tenantId,
|
|
445
|
+
publishedAt: args.publishedAt,
|
|
446
|
+
...(image.buildLogsUrl !== undefined && { buildLogsUrl: image.buildLogsUrl }),
|
|
447
|
+
...(signature !== undefined && { signature }),
|
|
448
|
+
});
|
|
449
|
+
const envelopePath = join(args.outDir, 'deploy-envelope.json');
|
|
450
|
+
await writeFile(envelopePath, `${JSON.stringify(envelope, null, 2)}\n`, 'utf8');
|
|
451
|
+
lines('');
|
|
452
|
+
lines(` Deploy envelope written to ${envelopePath}`);
|
|
453
|
+
lines(` Run \`kindgi deploy --env=${args.envName}\` to POST it.`);
|
|
454
|
+
lines('');
|
|
455
|
+
return { kind: 'ok', envelopePath, signature };
|
|
456
|
+
}
|
|
457
|
+
/** A failure that still shows what the build did up to it. */
|
|
458
|
+
function withBanner(result, banner) {
|
|
459
|
+
return { ...result, stderr: `${banner.join('\n')}\n${result.stderr}` };
|
|
460
|
+
}
|
|
461
|
+
/**
|
|
462
|
+
* A Node pack: how the image installs the app (`host-install.ts`), its
|
|
463
|
+
* base image, the bundles, the local index over them, the Containerfile,
|
|
464
|
+
* and the build context (`<out>/context`).
|
|
465
|
+
*/
|
|
466
|
+
async function prepareNodeContext(runners, args, expectedIndexPath, lines) {
|
|
467
|
+
const hostInstall = await resolveHostInstall(args.packDir);
|
|
468
|
+
if (hostInstall.kind === 'err')
|
|
469
|
+
return failure(`${hostInstall.message}\n`);
|
|
470
|
+
const install = hostInstall.install;
|
|
471
|
+
const base = nodeBaseImageFor(install.enginesNode);
|
|
472
|
+
if (base.kind === 'err')
|
|
473
|
+
return failure(`${base.message}\n`);
|
|
474
|
+
const configFile = await findKindgiConfig(args.packDir);
|
|
475
|
+
if (configFile === undefined || configFile.format === 'pyproject') {
|
|
476
|
+
return failure(`No kindgi.config.* at ${args.packDir}.\n`);
|
|
477
|
+
}
|
|
478
|
+
const includes = readBundleConfig(args.config);
|
|
479
|
+
if (includes.kind === 'invalid')
|
|
480
|
+
return failure(`${includes.message}\n`);
|
|
481
|
+
const includeFiles = await collectIncludeFiles(args.packDir, includes.bundle.include);
|
|
482
|
+
if (includeFiles.kind === 'error')
|
|
483
|
+
return failure(`${includeFiles.message}\n`);
|
|
484
|
+
const imageConfig = readImageConfig(args.config);
|
|
485
|
+
if (imageConfig.kind === 'invalid')
|
|
486
|
+
return failure(`${imageConfig.message}\n`);
|
|
487
|
+
const image = imageConfig.image;
|
|
488
|
+
for (const file of image.contextFiles) {
|
|
489
|
+
if (!(await isFile(join(args.packDir, file)))) {
|
|
490
|
+
return failure(`image: ${file} (an extension's file) doesn't exist in ${args.packDir}.\n`);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
lines(` Install: ${install.managerSpec ?? install.manager} from ${install.files.find((f) => /lock|shrinkwrap/.test(f)) ?? 'the lockfile'}${install.packRel === '' ? '' : ` (pack at ${install.packRel}/)`}`);
|
|
494
|
+
lines(` Node: ${base.image.version} (${base.image.ref.split('@')[0]})`);
|
|
495
|
+
if (image.extensions.length > 0 || image.systemPackages.length > 0) {
|
|
496
|
+
lines(` Image: ${[
|
|
497
|
+
...image.extensions.map((e) => `${e} extension`),
|
|
498
|
+
...(image.systemPackages.length > 0 ? [`apt: ${image.systemPackages.join(' ')}`] : []),
|
|
499
|
+
].join('; ')}`);
|
|
500
|
+
}
|
|
501
|
+
// ---- 1. esbuild bundle -----------------------------------------------
|
|
502
|
+
lines(' Bundling (esbuild — local)');
|
|
503
|
+
const bundleDir = join(args.outDir, 'dist');
|
|
504
|
+
const bundle = await runners.esbuildBundle({
|
|
505
|
+
packDir: args.packDir,
|
|
506
|
+
outputDir: bundleDir,
|
|
507
|
+
// The config's discovery patterns (defaults filled in) — the same
|
|
508
|
+
// files the indexer discovers.
|
|
509
|
+
discoveryPatterns: discoveryPatternsOf(args.config),
|
|
510
|
+
configPath: configFile.path,
|
|
511
|
+
nodeMajor: base.image.major,
|
|
512
|
+
});
|
|
513
|
+
lines(` ✓ ${Object.keys(bundle.bundleMap).length} pack modules, the config, and the framework entrypoints (kindgi-index + the pack service)`);
|
|
514
|
+
lines(` ✓ ${bundle.emitted.length} files emitted (${humanBytes(bundle.totalBytes)})`);
|
|
515
|
+
lines(renderExternalsSummary(bundle.externals));
|
|
516
|
+
const devOnly = devOnlyImports(bundle.externals, install.packDependencies);
|
|
517
|
+
if (devOnly.length > 0) {
|
|
518
|
+
const manifest = install.packRel === '' ? 'package.json' : `${install.packRel}/package.json`;
|
|
519
|
+
const one = devOnly.length === 1;
|
|
520
|
+
return failure(`The pack imports ${devOnly.join(', ')}, which ${manifest} lists only in devDependencies. The image keeps production dependencies only, so ${one ? 'it' : 'they'} wouldn't load there: move ${one ? 'it' : 'them'} to dependencies.\n`);
|
|
521
|
+
}
|
|
522
|
+
// ---- 2. Local indexer over the bundles ----------------------------------
|
|
523
|
+
const localIndex = await runners.runLocalIndexer({
|
|
524
|
+
packDir: args.packDir,
|
|
525
|
+
bundleDir,
|
|
526
|
+
outputPath: expectedIndexPath,
|
|
527
|
+
artifactVersion: args.artifactVersion,
|
|
528
|
+
publishedAt: args.publishedAt,
|
|
529
|
+
});
|
|
530
|
+
if (localIndex.kind === 'err') {
|
|
531
|
+
return failure(`Local indexer failed: [${localIndex.code}] ${localIndex.message}${localIndex.filePath !== undefined ? ` (at ${localIndex.filePath})` : ''}\n`);
|
|
532
|
+
}
|
|
533
|
+
if (localIndex.fileErrors.length > 0) {
|
|
534
|
+
return failure(`Local indexer reported file errors — fix them before building:\n${localIndex.fileErrors
|
|
535
|
+
.map((e) => ` [${e.code}] ${e.message}`)
|
|
536
|
+
.join('\n')}\n`);
|
|
537
|
+
}
|
|
538
|
+
lines(` ✓ ${localIndex.counts.tools} tools, ${localIndex.counts.guardrails} guardrails, ` +
|
|
539
|
+
`${localIndex.counts.agents} agents, ${localIndex.counts.flows} flows indexed`);
|
|
540
|
+
// ---- 3. Containerfile + context --------------------------------------
|
|
541
|
+
const containerfilePath = join(args.outDir, 'Containerfile');
|
|
542
|
+
await runners.writeContainerfile({
|
|
543
|
+
outputPath: containerfilePath,
|
|
544
|
+
artifactVersion: args.artifactVersion,
|
|
545
|
+
publishedAt: args.publishedAt,
|
|
546
|
+
baseImageRef: base.image.ref,
|
|
547
|
+
buildTarget: args.buildTarget,
|
|
548
|
+
install,
|
|
549
|
+
image,
|
|
550
|
+
hasIncludes: includeFiles.files.length > 0,
|
|
551
|
+
});
|
|
552
|
+
const contextDir = join(args.outDir, 'context');
|
|
553
|
+
await runners.writeContext({
|
|
554
|
+
contextDir,
|
|
555
|
+
packDir: args.packDir,
|
|
556
|
+
install,
|
|
557
|
+
bundleDir,
|
|
558
|
+
containerfilePath,
|
|
559
|
+
includes: includeFiles.files,
|
|
560
|
+
extensionFiles: image.contextFiles,
|
|
561
|
+
});
|
|
562
|
+
lines(` ✓ Context: ${install.files.length} install file(s), the bundles${includeFiles.files.length > 0 ? `, ${includeFiles.files.length} bundle.include file(s)` : ''}; no pack source`);
|
|
563
|
+
if (install.secrets.length > 0) {
|
|
564
|
+
lines(` ✓ ${install.secrets.map((s) => s.file).join(', ')}: read by the install as a build secret, never in the image`);
|
|
565
|
+
}
|
|
566
|
+
for (const line of skippedScriptLines(install.skippedScripts))
|
|
567
|
+
lines(line);
|
|
568
|
+
return { kind: 'ok', contextDir, counts: localIndex.counts, install };
|
|
569
|
+
}
|
|
570
|
+
/**
|
|
571
|
+
* What the image leaves out of the app's own install scripts, and what
|
|
572
|
+
* to do when one of them is something the image needs: `patch-package`
|
|
573
|
+
* (pnpm's own patches are applied by the install).
|
|
574
|
+
*/
|
|
575
|
+
export function skippedScriptLines(skipped) {
|
|
576
|
+
if (skipped.length === 0)
|
|
577
|
+
return [];
|
|
578
|
+
const where = (s) => s.manifest === 'package.json' ? s.name : `${s.manifest} ${s.name}`;
|
|
579
|
+
const lines = [
|
|
580
|
+
` ✓ The app's own install scripts don't run in the image: ${skipped.map((s) => `${where(s)} (\`${s.command}\`)`).join(', ')}`,
|
|
581
|
+
];
|
|
582
|
+
for (const s of skipped.filter((s) => /\bpatch-package\b/.test(s.command))) {
|
|
583
|
+
lines.push(` ⚠ ${where(s)} runs patch-package, so its patches aren't applied in the image. Apply them with a build step: defineBuildExtension({ name: 'patch-package', contextFiles: [<each file in patches/>], postInstall: [{ bin: 'patch-package' }] }) in image.extensions (@kindgi/sdk/build). With pnpm, pnpm patch applies them in the install itself.`);
|
|
584
|
+
}
|
|
585
|
+
return lines;
|
|
586
|
+
}
|
|
587
|
+
/** A Node pack's context, tarred for the build service. */
|
|
588
|
+
async function prepareNodeTarball(runners, args, expectedIndexPath, lines) {
|
|
589
|
+
const context = await prepareNodeContext(runners, args, expectedIndexPath, lines);
|
|
590
|
+
if (context.kind === 'error')
|
|
591
|
+
return context;
|
|
592
|
+
if (context.install.secrets.length > 0) {
|
|
593
|
+
lines(` ⚠ The build service gets no build secrets: an install that needs ${context.install.secrets.map((s) => s.file).join(', ')} (a private registry) fails there. \`kindgi build --local\` passes them.`);
|
|
594
|
+
}
|
|
595
|
+
const tarballPath = join(args.outDir, 'pack.tgz');
|
|
596
|
+
const tarball = await runners.tarPack({
|
|
597
|
+
contextDir: context.contextDir,
|
|
598
|
+
outputPath: tarballPath,
|
|
599
|
+
});
|
|
600
|
+
const tarballHash = await sha256Hex(tarball.bytes);
|
|
601
|
+
lines(` ✓ Emitted ${tarballPath} (${humanBytes(tarball.size)}, ${tarballHash.slice(0, 14)}…)`);
|
|
602
|
+
return { kind: 'ok', tarball, tarballHash, counts: context.counts };
|
|
603
|
+
}
|
|
604
|
+
/**
|
|
605
|
+
* A Python pack: the local index with the pack's interpreter, the Python
|
|
606
|
+
* Containerfile (`build/python-image.ts`), the pack root as the context.
|
|
607
|
+
*/
|
|
608
|
+
async function preparePythonContext(ctx, runners, args, expectedIndexPath, lines) {
|
|
609
|
+
const python = runners.python;
|
|
610
|
+
if (python === undefined)
|
|
611
|
+
return failure('This CLI cannot build Python packs (no Python build runners).\n');
|
|
612
|
+
const interpreter = await resolvePackPython(args.packDir, args.config);
|
|
613
|
+
if (interpreter.kind === 'err')
|
|
614
|
+
return failure(`kindgi build: ${interpreter.message}\n`);
|
|
615
|
+
const env = {};
|
|
616
|
+
for (const name of ['PATH', 'HOME', 'TMPDIR']) {
|
|
617
|
+
const value = ctx.env[name];
|
|
618
|
+
if (value !== undefined)
|
|
619
|
+
env[name] = value;
|
|
620
|
+
}
|
|
621
|
+
const localIndex = await python.runLocalIndexer({
|
|
622
|
+
packDir: args.packDir,
|
|
623
|
+
outputPath: expectedIndexPath,
|
|
624
|
+
artifactVersion: args.artifactVersion,
|
|
625
|
+
publishedAt: args.publishedAt,
|
|
626
|
+
python: interpreter.value,
|
|
627
|
+
env,
|
|
628
|
+
});
|
|
629
|
+
if (localIndex.kind === 'err') {
|
|
630
|
+
return failure(`Local indexer failed: [${localIndex.code}] ${localIndex.message}${localIndex.filePath !== undefined ? ` (at ${localIndex.filePath})` : ''}\n`);
|
|
631
|
+
}
|
|
632
|
+
if (localIndex.fileErrors.length > 0) {
|
|
633
|
+
return failure(`Local indexer reported file errors — fix them before building:\n${localIndex.fileErrors
|
|
634
|
+
.map((e) => ` [${e.code}] ${e.message}`)
|
|
635
|
+
.join('\n')}\n`);
|
|
636
|
+
}
|
|
637
|
+
lines(` Indexing (Python — ${interpreter.value.join(' ')})`);
|
|
638
|
+
lines(` ✓ ${localIndex.counts.tools} tools, ${localIndex.counts.guardrails} guardrails, ` +
|
|
639
|
+
`${localIndex.counts.agents} agents, ${localIndex.counts.flows} flows discovered`);
|
|
640
|
+
const context = await collectPythonContextFiles(args.packDir);
|
|
641
|
+
if (context.kind === 'error')
|
|
642
|
+
return failure(`${context.message}\n`);
|
|
643
|
+
const image = args.config.image;
|
|
644
|
+
const system = checkAptPackages(image !== null && typeof image === 'object'
|
|
645
|
+
? image['system-packages']
|
|
646
|
+
: undefined, '[tool.kindgi.image] system-packages');
|
|
647
|
+
if (system.kind === 'err')
|
|
648
|
+
return failure(`kindgi build: ${system.message}\n`);
|
|
649
|
+
const containerfilePath = join(args.outDir, 'Containerfile');
|
|
650
|
+
await python.writeContainerfile({
|
|
651
|
+
outputPath: containerfilePath,
|
|
652
|
+
artifactVersion: args.artifactVersion,
|
|
653
|
+
publishedAt: args.publishedAt,
|
|
654
|
+
baseImageRef: DEFAULT_PYTHON_BASE_IMAGE_REF,
|
|
655
|
+
uvImageRef: DEFAULT_UV_IMAGE_REF,
|
|
656
|
+
buildTarget: args.buildTarget,
|
|
657
|
+
installer: context.installer,
|
|
658
|
+
systemPackages: system.packages,
|
|
659
|
+
});
|
|
660
|
+
lines(` ✓ ${context.files.length} pack file(s) in the image (the pack root, minus caches, virtualenvs and secrets); dependencies from ${PYTHON_LOCKFILES[context.installer]}${system.packages.length > 0 ? `; Debian packages: ${system.packages.join(', ')}` : ''}`);
|
|
661
|
+
const tarballPath = join(args.outDir, 'pack.tgz');
|
|
662
|
+
const tarball = await python.tarContext({
|
|
663
|
+
packDir: args.packDir,
|
|
664
|
+
files: context.files,
|
|
665
|
+
containerfilePath,
|
|
666
|
+
outputPath: tarballPath,
|
|
667
|
+
});
|
|
668
|
+
const tarballHash = await sha256Hex(tarball.bytes);
|
|
669
|
+
lines(` ✓ Emitted ${tarballPath} (${humanBytes(tarball.size)}, ${tarballHash.slice(0, 14)}…)`);
|
|
670
|
+
return { kind: 'ok', tarball, tarballHash, counts: localIndex.counts };
|
|
671
|
+
}
|
|
672
|
+
async function resolveBuildArgs(ctx) {
|
|
673
|
+
// --path — pack root; auto-detects repo root separately so augment-
|
|
674
|
+
// mode packs (kindgi.config.ts under <repo>/kindgi/, package.json at
|
|
675
|
+
// <repo>/) build correctly. See src/build/pack-root.ts for the
|
|
676
|
+
// algorithm.
|
|
677
|
+
const pathFlag = typeof ctx.options.path === 'string' ? ctx.options.path : undefined;
|
|
678
|
+
const rootsResult = await resolvePackRoots({
|
|
679
|
+
cwd: ctx.cwd,
|
|
680
|
+
...(pathFlag !== undefined && pathFlag !== '' && { pathFlag }),
|
|
681
|
+
});
|
|
682
|
+
if (rootsResult.kind === 'err') {
|
|
683
|
+
return { kind: 'error', stderr: `${rootsResult.message}\n`, exitCode: 1 };
|
|
684
|
+
}
|
|
685
|
+
const { packDir, repoRoot, mode: packMode, language } = rootsResult.roots;
|
|
686
|
+
const local = ctx.options.local === true;
|
|
687
|
+
const push = typeof ctx.options.push === 'string' ? ctx.options.push : undefined;
|
|
688
|
+
if (push !== undefined && !local) {
|
|
689
|
+
return {
|
|
690
|
+
kind: 'error',
|
|
691
|
+
stderr: '--push goes with --local: `kindgi build --local --push [<repository>]`.\n',
|
|
692
|
+
exitCode: 1,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
if (local && language === 'python') {
|
|
696
|
+
return {
|
|
697
|
+
kind: 'error',
|
|
698
|
+
stderr: 'kindgi build --local builds TypeScript packs; build a Python pack with the build service.\n',
|
|
699
|
+
exitCode: 1,
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
// Load kindgi.config.ts. Node 22's `--experimental-strip-types` is
|
|
703
|
+
// enabled by the shebang in production; tests inject a
|
|
704
|
+
// `configLoader` seam.
|
|
705
|
+
const configResult = await loadBuildConfig(ctx, packDir);
|
|
706
|
+
if (configResult.kind === 'error')
|
|
707
|
+
return configResult;
|
|
708
|
+
const config = configResult.config;
|
|
709
|
+
// --env — resolves an env block; default 'staging'.
|
|
710
|
+
const envFlag = ctx.options.env;
|
|
711
|
+
const envName = typeof envFlag === 'string' && envFlag !== '' ? envFlag : 'staging';
|
|
712
|
+
const envBlock = config.environments?.[envName] !== undefined
|
|
713
|
+
? config.environments[envName]
|
|
714
|
+
: undefined;
|
|
715
|
+
// --target — falls back to the env name.
|
|
716
|
+
const targetFlag = ctx.options.target;
|
|
717
|
+
const buildTarget = typeof targetFlag === 'string' && targetFlag !== ''
|
|
718
|
+
? targetFlag
|
|
719
|
+
: (envBlock?.buildTarget ?? envName);
|
|
720
|
+
// --endpoint — flag > env block > error.
|
|
721
|
+
const endpointFlag = ctx.options.endpoint;
|
|
722
|
+
const endpoint = typeof endpointFlag === 'string' && endpointFlag !== ''
|
|
723
|
+
? endpointFlag
|
|
724
|
+
: (envBlock?.build ?? '');
|
|
725
|
+
if (endpoint === '' && !local) {
|
|
726
|
+
return {
|
|
727
|
+
kind: 'error',
|
|
728
|
+
stderr: `kindgi build could not resolve a build-server endpoint. Set environments.${envName}.build in kindgi.config.ts or pass --endpoint <url>.\n`,
|
|
729
|
+
exitCode: 1,
|
|
730
|
+
};
|
|
731
|
+
}
|
|
732
|
+
// --out — output dir.
|
|
733
|
+
const outFlag = ctx.options.out;
|
|
734
|
+
const outDirRaw = typeof outFlag === 'string' && outFlag !== '' ? outFlag : DEFAULT_OUT_DIR;
|
|
735
|
+
const outDir = isAbsolute(outDirRaw) ? outDirRaw : resolve(packDir, outDirRaw);
|
|
736
|
+
// --artifact-version — pinned or auto YYYYMMDD.1.
|
|
737
|
+
const avFlag = ctx.options['artifact-version'];
|
|
738
|
+
const artifactVersion = typeof avFlag === 'string' && avFlag !== '' ? avFlag : defaultArtifactVersion();
|
|
739
|
+
// --published-at — SOURCE_DATE_EPOCH=0 baseline unless overridden.
|
|
740
|
+
const paFlag = ctx.options['published-at'];
|
|
741
|
+
const publishedAt = typeof paFlag === 'string' && paFlag !== '' ? paFlag : DEFAULT_PUBLISHED_AT;
|
|
742
|
+
// --tenant — flag > env block > KINDGI_TENANT_ID env var > error.
|
|
743
|
+
const tenantFlag = ctx.options.tenant;
|
|
744
|
+
const tenantId = typeof tenantFlag === 'string' && tenantFlag !== ''
|
|
745
|
+
? tenantFlag
|
|
746
|
+
: (envBlock?.tenantId ??
|
|
747
|
+
(typeof ctx.env.KINDGI_TENANT_ID === 'string'
|
|
748
|
+
? ctx.env.KINDGI_TENANT_ID
|
|
749
|
+
: undefined));
|
|
750
|
+
if ((tenantId === undefined || tenantId === '') && (!local || push !== undefined)) {
|
|
751
|
+
return {
|
|
752
|
+
kind: 'error',
|
|
753
|
+
stderr: `kindgi build could not resolve a tenantId. Set environments.${envName}.tenantId in kindgi.config.ts, pass --tenant <id>, or export KINDGI_TENANT_ID.\n`,
|
|
754
|
+
exitCode: 1,
|
|
755
|
+
};
|
|
756
|
+
}
|
|
757
|
+
// --skip-integrity-gate / --skip-image-pull / --skip-sign
|
|
758
|
+
const skipIntegrityGate = ctx.options['skip-integrity-gate'] === true;
|
|
759
|
+
const skipImagePull = ctx.options['skip-image-pull'] === true;
|
|
760
|
+
const skipSign = ctx.options['skip-sign'] === true;
|
|
761
|
+
// --signing-key — flag > env block; error only if we're actually
|
|
762
|
+
// signing (skipSign flips this off).
|
|
763
|
+
const signKeyFlag = ctx.options['signing-key'];
|
|
764
|
+
const signingKeyRaw = typeof signKeyFlag === 'string' && signKeyFlag !== '' ? signKeyFlag : envBlock?.signingKey;
|
|
765
|
+
const signingKeyPath = typeof signingKeyRaw === 'string' && signingKeyRaw !== ''
|
|
766
|
+
? expandHome(signingKeyRaw, ctx.home)
|
|
767
|
+
: undefined;
|
|
768
|
+
// --signer-key-id — derive from key filename basename if not
|
|
769
|
+
// explicitly configured.
|
|
770
|
+
const keyIdFlag = ctx.options['signer-key-id'];
|
|
771
|
+
const signerKeyId = typeof keyIdFlag === 'string' && keyIdFlag !== ''
|
|
772
|
+
? keyIdFlag
|
|
773
|
+
: (envBlock?.signerKeyId ??
|
|
774
|
+
(signingKeyPath !== undefined ? basenameNoExt(signingKeyPath) : `${envName}-signer`));
|
|
775
|
+
// --push — the repository: the flag's, else the env block's registry
|
|
776
|
+
// plus the pack id.
|
|
777
|
+
const packId = config.pack?.id;
|
|
778
|
+
const pushRepository = push === undefined
|
|
779
|
+
? undefined
|
|
780
|
+
: push !== ''
|
|
781
|
+
? push
|
|
782
|
+
: envBlock?.registry !== undefined && typeof packId === 'string'
|
|
783
|
+
? `${envBlock.registry.replace(/\/+$/, '')}/${packId}`
|
|
784
|
+
: undefined;
|
|
785
|
+
if (push !== undefined && pushRepository === undefined) {
|
|
786
|
+
return {
|
|
787
|
+
kind: 'error',
|
|
788
|
+
stderr: `kindgi build --push needs a repository: set environments.${envName}.registry in kindgi.config.ts, or pass --push=<repository>.\n`,
|
|
789
|
+
exitCode: 1,
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
const platformFlag = ctx.options.platform;
|
|
793
|
+
const platform = typeof platformFlag === 'string' && platformFlag !== ''
|
|
794
|
+
? platformFlag
|
|
795
|
+
: push !== undefined
|
|
796
|
+
? 'linux/amd64'
|
|
797
|
+
: undefined;
|
|
798
|
+
// --registry-push-creds — opaque reference, empty means undefined.
|
|
799
|
+
const credsFlag = ctx.options['registry-push-creds'];
|
|
800
|
+
const registryPushCredsRef = typeof credsFlag === 'string' && credsFlag !== '' ? credsFlag : undefined;
|
|
801
|
+
return {
|
|
802
|
+
kind: 'ok',
|
|
803
|
+
args: {
|
|
804
|
+
packDir,
|
|
805
|
+
repoRoot,
|
|
806
|
+
packMode,
|
|
807
|
+
language,
|
|
808
|
+
outDir,
|
|
809
|
+
envName,
|
|
810
|
+
endpoint,
|
|
811
|
+
buildTarget,
|
|
812
|
+
registryPushCredsRef,
|
|
813
|
+
artifactVersion,
|
|
814
|
+
publishedAt,
|
|
815
|
+
tenantId: tenantId ?? '',
|
|
816
|
+
signingKeyPath,
|
|
817
|
+
signerKeyId,
|
|
818
|
+
skipIntegrityGate,
|
|
819
|
+
skipImagePull,
|
|
820
|
+
skipSign,
|
|
821
|
+
local,
|
|
822
|
+
pushRepository,
|
|
823
|
+
platform,
|
|
824
|
+
config,
|
|
825
|
+
},
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
async function loadBuildConfig(ctx, packDir) {
|
|
829
|
+
const outcome = await loadPackConfig(ctx, packDir);
|
|
830
|
+
if (outcome.kind === 'ok')
|
|
831
|
+
return { kind: 'ok', config: outcome.config };
|
|
832
|
+
return {
|
|
833
|
+
kind: 'error',
|
|
834
|
+
stderr: outcome.kind === 'missing'
|
|
835
|
+
? `kindgi build could not load kindgi.config.ts at ${packDir}. Run \`kindgi init <pack-name>\` to scaffold a pack, or pass --path=<dir>.\n`
|
|
836
|
+
: `Failed to load kindgi.config.ts at ${packDir}: ${outcome.message}\n`,
|
|
837
|
+
exitCode: 1,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
// ---------------------------------------------------------------------
|
|
841
|
+
// Small helpers
|
|
842
|
+
// ---------------------------------------------------------------------
|
|
843
|
+
function failure(stderr) {
|
|
844
|
+
return { kind: 'error', stderr, exitCode: 1 };
|
|
845
|
+
}
|
|
846
|
+
/**
|
|
847
|
+
* The packages the bundles load from the app's `node_modules`, for the
|
|
848
|
+
* summary. Empty list → empty string (caller emits no extra line).
|
|
849
|
+
*/
|
|
850
|
+
function renderExternalsSummary(externals) {
|
|
851
|
+
if (externals.length === 0)
|
|
852
|
+
return '';
|
|
853
|
+
const suffix = externals.length === 1 ? '' : 's';
|
|
854
|
+
return ` ✓ ${externals.length} package${suffix} from the app's node_modules: ${externals.join(', ')}`;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Read `config.discovery` and extract folder prefixes for the
|
|
858
|
+
* esbuild bundler to walk. Handles both shapes:
|
|
859
|
+
*
|
|
860
|
+
* discovery: { tools: 'tools/**\/*.ts', ... } → ['tools', ...]
|
|
861
|
+
* discovery: { tools: 'kindgi/tools/** The config's discovery patterns with defaults filled in — what the indexer uses. */
|
|
862
|
+
export function discoveryPatternsOf(config) {
|
|
863
|
+
const discovery = config.discovery;
|
|
864
|
+
return Object.values(resolveDiscovery(typeof discovery === 'object' && discovery !== null
|
|
865
|
+
? discovery
|
|
866
|
+
: undefined));
|
|
867
|
+
}
|
|
868
|
+
function defaultArtifactVersion() {
|
|
869
|
+
const now = new Date();
|
|
870
|
+
const yyyy = now.getUTCFullYear();
|
|
871
|
+
const mm = String(now.getUTCMonth() + 1).padStart(2, '0');
|
|
872
|
+
const dd = String(now.getUTCDate()).padStart(2, '0');
|
|
873
|
+
return `${yyyy}${mm}${dd}.1`;
|
|
874
|
+
}
|
|
875
|
+
function expandHome(path, home) {
|
|
876
|
+
if (!path.startsWith('~'))
|
|
877
|
+
return path;
|
|
878
|
+
const h = home ?? process.env.HOME ?? '';
|
|
879
|
+
return `${h}${path.slice(1)}`;
|
|
880
|
+
}
|
|
881
|
+
function basenameNoExt(path) {
|
|
882
|
+
const idx = Math.max(path.lastIndexOf('/'), path.lastIndexOf('\\'));
|
|
883
|
+
const base = idx === -1 ? path : path.slice(idx + 1);
|
|
884
|
+
const dot = base.lastIndexOf('.');
|
|
885
|
+
return dot === -1 ? base : base.slice(0, dot);
|
|
886
|
+
}
|
|
887
|
+
function humanBytes(n) {
|
|
888
|
+
if (n < 1024)
|
|
889
|
+
return `${n} B`;
|
|
890
|
+
if (n < 1024 * 1024)
|
|
891
|
+
return `${(n / 1024).toFixed(1)} KB`;
|
|
892
|
+
return `${(n / (1024 * 1024)).toFixed(1)} MB`;
|
|
893
|
+
}
|
|
894
|
+
async function isFile(path) {
|
|
895
|
+
try {
|
|
896
|
+
return (await stat(path)).isFile();
|
|
897
|
+
}
|
|
898
|
+
catch {
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
//# sourceMappingURL=build.js.map
|