@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,1032 @@
|
|
|
1
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
// Copyright (C) 2026 Kindgi Inc.
|
|
3
|
+
/**
|
|
4
|
+
* `kindgi dev` — the local development loop.
|
|
5
|
+
*
|
|
6
|
+
* One command, everything on the laptop. Runs the Kindgi runtime as a
|
|
7
|
+
* container (`dev/runtime-container.ts`; or attaches to one you run,
|
|
8
|
+
* `--runtime-url`), discovers the pack's primitives via
|
|
9
|
+
* `@kindgi/handler-runtime.runIndexer`, runs the pack's code in a local
|
|
10
|
+
* pack service (the same process a deployment runs, as a child),
|
|
11
|
+
* registers each primitive into the runtime via the SDK, watches the
|
|
12
|
+
* four discovery folders (`tools/`, `guardrails/`, `agents/`, `flows/`)
|
|
13
|
+
* and the env files, and re-indexes, restarts the pack service and
|
|
14
|
+
* re-registers on change.
|
|
15
|
+
*
|
|
16
|
+
* v1 constraints:
|
|
17
|
+
* - Docker required for the runtime (unless `--runtime-url`), and
|
|
18
|
+
* Postgres: `kindgi dev` starts a bundled `docker-compose.dev.yml`
|
|
19
|
+
* (Postgres) with `docker compose`, shared by every `kindgi dev` on
|
|
20
|
+
* the machine and left running; or set `--database-url` /
|
|
21
|
+
* `KINDGI_DATABASE_URL`.
|
|
22
|
+
* - `--watch` defaults ON. `--no-watch` runs a single boot + register
|
|
23
|
+
* cycle then exits — useful for smoke tests + CI.
|
|
24
|
+
* - A watch tick re-indexes and reloads the pack. The runtime serves
|
|
25
|
+
* the primitives from the pack's index on disk, so nothing is
|
|
26
|
+
* registered over HTTP.
|
|
27
|
+
*
|
|
28
|
+
* Every side effect (server boot, indexer run, filesystem watch)
|
|
29
|
+
* flows through `ctx.devRunners` so tests can substitute fixtures
|
|
30
|
+
* without booting Postgres or hitting the filesystem.
|
|
31
|
+
*/
|
|
32
|
+
import { randomBytes, randomUUID } from 'node:crypto';
|
|
33
|
+
import { existsSync } from 'node:fs';
|
|
34
|
+
import { chmod, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
35
|
+
import { isAbsolute, join, resolve } from 'node:path';
|
|
36
|
+
import { discoveryRoots, findKindgiConfig, packLanguage, resolveDiscovery, } from '@kindgi/handler-runtime';
|
|
37
|
+
import { LOCAL_ENV_NAME, displayEnvPath, packValues, readPackEnv, runtimeValues, } from '@kindgi/secrets-dotenv';
|
|
38
|
+
import { CORS_ORIGINS_VAR, PUBLIC_TOKEN_KEY_PATH_VAR, parseCorsOrigins } from '@kindgi/env-schema';
|
|
39
|
+
import { resolvePackCode } from '../dev/pack-code.js';
|
|
40
|
+
import { devPackEnv, devPackEnvFiles } from '../dev/pack-env.js';
|
|
41
|
+
import { createPackRefresher, describePackEvent } from '../dev/pack-service.js';
|
|
42
|
+
import { registerFromIndex } from '../dev/register.js';
|
|
43
|
+
import { DEFAULT_RUNTIME_IMAGE } from '../dev/runtime-image.js';
|
|
44
|
+
import { describeEnvDiagnostics, loadLocalEnvSettings } from '../env/project-env.js';
|
|
45
|
+
import { renderJson } from '../output.js';
|
|
46
|
+
import { binDisplay, detectBinRunner } from '../package-manager.js';
|
|
47
|
+
import { defaultSdkSkillsRoot } from './init.js';
|
|
48
|
+
import { detectSkillDrift } from './skills.js';
|
|
49
|
+
export const devCommand = {
|
|
50
|
+
kind: 'leaf',
|
|
51
|
+
name: 'dev',
|
|
52
|
+
description: 'Run the Kindgi runtime as a container + hot-reload the pack under cwd.',
|
|
53
|
+
usage: 'kindgi dev [--port <n>] [--database-url <url>] [--tenant <id>] [--dev-token <token>] [--no-watch] [--path <dir>] [--reset] [--recreate-services] [--runtime-image <ref> | --runtime-url <url>]',
|
|
54
|
+
optionSpec: {
|
|
55
|
+
port: {
|
|
56
|
+
type: 'string',
|
|
57
|
+
description: "The port the runtime's API is reached on, on `127.0.0.1`. Default: `4000`. Not used with `--runtime-url`.",
|
|
58
|
+
},
|
|
59
|
+
// The Kindgi runtime image to run (default: the one this CLI release pins).
|
|
60
|
+
'runtime-image': {
|
|
61
|
+
type: 'string',
|
|
62
|
+
description: 'The runtime image to run. Default: the one this CLI release pins.',
|
|
63
|
+
},
|
|
64
|
+
// A runtime you run yourself (e.g. from source) instead of the image.
|
|
65
|
+
'runtime-url': {
|
|
66
|
+
type: 'string',
|
|
67
|
+
description: "Use a runtime you run yourself, at this origin, instead of starting the container. Start it with the pack's `.kindgi/dev/runtime.env`.",
|
|
68
|
+
},
|
|
69
|
+
'database-url': {
|
|
70
|
+
type: 'string',
|
|
71
|
+
description: "The Postgres to use. Default: `KINDGI_DATABASE_URL` (the shell's, then the env files'), else the bundled Postgres.",
|
|
72
|
+
},
|
|
73
|
+
tenant: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: "Pin the tenant. Default: the previous run's (from `.kindgirc.json`), else a new one.",
|
|
76
|
+
},
|
|
77
|
+
// Named `dev-token` to avoid clobbering the global `--token`
|
|
78
|
+
// (which is the caller's bearer token to a remote API). In dev
|
|
79
|
+
// mode, we CREATE a token for the freshly-booted api-server; this
|
|
80
|
+
// flag lets the caller pin it.
|
|
81
|
+
'dev-token': {
|
|
82
|
+
type: 'string',
|
|
83
|
+
description: "Pin the API token. Default: the previous run's (from `.kindgirc.json`), else a new one.",
|
|
84
|
+
},
|
|
85
|
+
watch: {
|
|
86
|
+
type: 'boolean',
|
|
87
|
+
description: 'Re-index and reload the pack on every save. On by default; `--no-watch` turns it off.',
|
|
88
|
+
},
|
|
89
|
+
'no-watch': {
|
|
90
|
+
type: 'boolean',
|
|
91
|
+
description: 'Start, index and register once, then exit. For smoke tests and CI.',
|
|
92
|
+
},
|
|
93
|
+
path: {
|
|
94
|
+
type: 'string',
|
|
95
|
+
description: 'The pack root, with a `kindgi.config.ts` or a `pyproject.toml` with `[tool.kindgi]`. Default: the current directory.',
|
|
96
|
+
},
|
|
97
|
+
// --reset: a fresh start for this pack only — a new tenant and token
|
|
98
|
+
// (it removes .kindgirc.json). The shared services and their data,
|
|
99
|
+
// which every kindgi dev on the machine uses, are never touched.
|
|
100
|
+
reset: {
|
|
101
|
+
type: 'boolean',
|
|
102
|
+
description: 'Start this pack fresh: removes `.kindgirc.json`, so the run gets a new tenant and token. The shared Postgres is left alone.',
|
|
103
|
+
},
|
|
104
|
+
// --recreate-services: let `docker compose` recreate the shared
|
|
105
|
+
// Postgres if its definition changed. Default: an existing container
|
|
106
|
+
// is reused as it is, so no other kindgi dev loses its database.
|
|
107
|
+
'recreate-services': {
|
|
108
|
+
type: 'boolean',
|
|
109
|
+
description: 'Let `docker compose` recreate the bundled Postgres if its definition changed. By default an existing container is reused.',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
run: async (ctx) => runDev(ctx),
|
|
113
|
+
};
|
|
114
|
+
/** The API's default port on `127.0.0.1`. */
|
|
115
|
+
export const DEFAULT_DEV_PORT = 4000;
|
|
116
|
+
/** Default debounce for the watcher's re-index tick. */
|
|
117
|
+
export const DEFAULT_WATCH_DEBOUNCE_MS = 200;
|
|
118
|
+
/**
|
|
119
|
+
* Render the "manual KINDGI_DATABASE_URL required" hint shown when
|
|
120
|
+
* (a) the caller passed neither `--database-url` nor `KINDGI_DATABASE_URL`
|
|
121
|
+
* AND (b) the auto-start path is not viable (no docker-compose on
|
|
122
|
+
* PATH, or the runner isn't wired). Includes the auto-start reason
|
|
123
|
+
* so the user knows what to fix.
|
|
124
|
+
*/
|
|
125
|
+
/**
|
|
126
|
+
* Live progress writer. Bypasses the command's buffered stdout/stderr
|
|
127
|
+
* return path so the developer sees phases as they happen — critical
|
|
128
|
+
* during the first-run docker-compose bring-up (image pulls,
|
|
129
|
+
* healthcheck waits) which can take 30–90s. Tests don't hit this
|
|
130
|
+
* path (the `startServices` runner is unwired in DevRunners fixtures)
|
|
131
|
+
* so nothing to intercept.
|
|
132
|
+
*/
|
|
133
|
+
function emitProgress(msg) {
|
|
134
|
+
process.stderr.write(` ${msg}\n`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Run an async work function while emitting a heartbeat every
|
|
138
|
+
* `intervalMs` so the user sees the seconds ticking during long
|
|
139
|
+
* operations (docker pulls, healthcheck waits). The heartbeat clears
|
|
140
|
+
* as soon as the work resolves or rejects.
|
|
141
|
+
*/
|
|
142
|
+
async function withHeartbeat(label, intervalMs, work) {
|
|
143
|
+
const started = Date.now();
|
|
144
|
+
const tick = setInterval(() => {
|
|
145
|
+
const elapsed = Math.round((Date.now() - started) / 1000);
|
|
146
|
+
process.stderr.write(` ⋯ ${label} (${elapsed}s elapsed)\n`);
|
|
147
|
+
}, intervalMs);
|
|
148
|
+
try {
|
|
149
|
+
return await work();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
clearInterval(tick);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function renderMissingDbHint(reason) {
|
|
156
|
+
return `kindgi dev needs Postgres. Auto-start not available: ${reason}\n\nOptions:\n 1. Install Docker Desktop / Colima so \`docker compose\` is on PATH — then just run \`kindgi dev\` and services start automatically.\n 2. Provide your own Postgres and set KINDGI_DATABASE_URL:\n docker run --rm -d --name kindgi-dev-pg -p 5432:5432 -e POSTGRES_PASSWORD=kindgi postgres:16\n export KINDGI_DATABASE_URL=postgres://postgres:kindgi@localhost:5432/postgres\n`;
|
|
157
|
+
}
|
|
158
|
+
export async function runDev(ctx) {
|
|
159
|
+
const parsed = resolveDevArgs(ctx);
|
|
160
|
+
if (parsed.kind === 'error')
|
|
161
|
+
return parsed;
|
|
162
|
+
const args = parsed.args;
|
|
163
|
+
const runners = pickRunners(ctx);
|
|
164
|
+
if (runners.kind === 'error')
|
|
165
|
+
return runners;
|
|
166
|
+
const dev = runners.runners;
|
|
167
|
+
// Confirm the pack has a kindgi.config.ts (or one of its
|
|
168
|
+
// accepted extensions). Failing loud here beats a cryptic
|
|
169
|
+
// config-not-found from the indexer.
|
|
170
|
+
const configOk = await ensurePackConfig(args.packDir);
|
|
171
|
+
if (!configOk.ok) {
|
|
172
|
+
return {
|
|
173
|
+
kind: 'error',
|
|
174
|
+
stderr: `kindgi dev could not find a kindgi.config.ts (or a pyproject.toml with a [tool.kindgi] table) at ${args.packDir}.\nRun \`kindgi init <pack-name>\` to scaffold a pack, or pass --path=<dir> to point at an existing one.\n`,
|
|
175
|
+
exitCode: 1,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
// The project's env files — Kindgi runtime config (`KINDGI_*`) for
|
|
179
|
+
// this process, everything else for the pack's agents via the dev
|
|
180
|
+
// secret binding.
|
|
181
|
+
const projectEnv = await loadDevProjectEnv(ctx, args.packDir);
|
|
182
|
+
if (projectEnv.kind === 'error')
|
|
183
|
+
return projectEnv;
|
|
184
|
+
const publicRunTokens = await resolveDevPublicRunTokens(ctx.env, projectEnv.runtime);
|
|
185
|
+
if (publicRunTokens.kind === 'error')
|
|
186
|
+
return publicRunTokens;
|
|
187
|
+
// `--reset` starts this pack fresh: without `.kindgirc.json` the boot
|
|
188
|
+
// below makes a new tenant and token, so the pack sees none of its
|
|
189
|
+
// earlier data. Other packs, and the shared services, are untouched.
|
|
190
|
+
if (args.reset) {
|
|
191
|
+
try {
|
|
192
|
+
await unlink(join(args.packDir, '.kindgirc.json'));
|
|
193
|
+
emitProgress('🧹 --reset: removed .kindgirc.json (fresh token + tenant this boot)');
|
|
194
|
+
}
|
|
195
|
+
catch (err) {
|
|
196
|
+
if (err.code !== 'ENOENT') {
|
|
197
|
+
emitProgress(` --reset: could not remove .kindgirc.json: ${err.message}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// Resolve the database URL. Precedence:
|
|
202
|
+
// 1. --database-url, then KINDGI_DATABASE_URL from the shell
|
|
203
|
+
// (resolved into args.databaseUrl at resolveDevArgs time).
|
|
204
|
+
// 2. KINDGI_DATABASE_URL from the project's env files.
|
|
205
|
+
// 3. Auto-start the bundled Postgres via
|
|
206
|
+
// `dev.startServices` when it's wired AND docker-compose is
|
|
207
|
+
// available.
|
|
208
|
+
// 4. Loud error with the manual quick-start hint.
|
|
209
|
+
let databaseUrl = args.databaseUrl ?? projectEnv.runtime.KINDGI_DATABASE_URL;
|
|
210
|
+
if (args.databaseUrl === undefined && databaseUrl !== undefined) {
|
|
211
|
+
emitProgress('using KINDGI_DATABASE_URL from the env files');
|
|
212
|
+
}
|
|
213
|
+
let servicesHandle;
|
|
214
|
+
if (databaseUrl === undefined) {
|
|
215
|
+
if (dev.startServices === undefined) {
|
|
216
|
+
return {
|
|
217
|
+
kind: 'error',
|
|
218
|
+
stderr: renderMissingDbHint('start-services runner not wired'),
|
|
219
|
+
exitCode: 1,
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (args.recreateServices) {
|
|
223
|
+
emitProgress('⚠ --recreate-services: the shared Postgres may be recreated; every kindgi dev on this machine using it reconnects');
|
|
224
|
+
}
|
|
225
|
+
emitProgress('Starting Postgres...');
|
|
226
|
+
emitProgress(' first-run image pull can take 30-90s; cached boots are ~5-10s');
|
|
227
|
+
const outcome = await withHeartbeat('still starting services', 5000, () => dev.startServices({ recreate: args.recreateServices }));
|
|
228
|
+
if (outcome.kind === 'unavailable') {
|
|
229
|
+
return {
|
|
230
|
+
kind: 'error',
|
|
231
|
+
stderr: renderMissingDbHint(outcome.reason),
|
|
232
|
+
exitCode: 1,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (outcome.kind === 'error') {
|
|
236
|
+
return {
|
|
237
|
+
kind: 'error',
|
|
238
|
+
stderr: `kindgi dev auto-started bundled services but the boot failed:\n ${outcome.message}\n\nFalling back to manual KINDGI_DATABASE_URL is your escape hatch — set KINDGI_DATABASE_URL then re-run.\n`,
|
|
239
|
+
exitCode: 1,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
servicesHandle = outcome.handle;
|
|
243
|
+
databaseUrl = outcome.handle.databaseUrl;
|
|
244
|
+
emitProgress(`✓ services ready: ${outcome.handle.services.join(', ')}`);
|
|
245
|
+
}
|
|
246
|
+
// Resolve the tenant and the bearer token BEFORE booting, so the
|
|
247
|
+
// server seeds the same ones across `kindgi dev` runs: the pack's
|
|
248
|
+
// registered primitives and providers stay with the tenant, and the
|
|
249
|
+
// console, curl bookmarks and anything else holding the printed token
|
|
250
|
+
// keep working. Each one, highest first:
|
|
251
|
+
// 1. Its flag (`--tenant`, `--dev-token`).
|
|
252
|
+
// 2. The previous boot's, from `.kindgirc.json` in the pack dir.
|
|
253
|
+
// 3. Server-generated on this boot (the very first one, or after
|
|
254
|
+
// `--reset`, which already deleted the file), then persisted.
|
|
255
|
+
// Per field: `--dev-token` alone keeps the previous tenant.
|
|
256
|
+
const kindgircPath = join(args.packDir, '.kindgirc.json');
|
|
257
|
+
const persisted = await readPersistedRc(kindgircPath);
|
|
258
|
+
// Start the runtime. A failed start is the loudest error path, so
|
|
259
|
+
// we tear down auto-started services (if any) before returning. The
|
|
260
|
+
// first boot (or the first after `--reset`) makes the tenant, token and
|
|
261
|
+
// user here; they're persisted after the runtime serves.
|
|
262
|
+
const effectiveTenantId = args.tenantId ?? persisted.tenantId ?? randomUUID();
|
|
263
|
+
const effectiveToken = args.token ?? persisted.token ?? generateDevToken();
|
|
264
|
+
// The user the token resolves to comes back with the tenant it
|
|
265
|
+
// belongs to: what is keyed to the user (a reviewer registration)
|
|
266
|
+
// survives restarts.
|
|
267
|
+
const effectiveUserId = (effectiveTenantId === persisted.tenantId ? persisted.userId : undefined) ?? randomUUID();
|
|
268
|
+
// The pack's code is bundled and runs in a local pack service. It
|
|
269
|
+
// starts once the pack is bundled and indexed; the api-server gets
|
|
270
|
+
// its transport now.
|
|
271
|
+
const packEnv = () => devPackEnv({
|
|
272
|
+
packDir: args.packDir,
|
|
273
|
+
...(projectEnv.localEnvFiles !== undefined && { localEnvFiles: projectEnv.localEnvFiles }),
|
|
274
|
+
hostEnv: ctx.env,
|
|
275
|
+
});
|
|
276
|
+
const code = await resolveDevPackCode(dev, projectEnv.language, args.packDir, projectEnv.config, packEnv);
|
|
277
|
+
if (code.kind === 'error') {
|
|
278
|
+
return code;
|
|
279
|
+
}
|
|
280
|
+
const packOptions = {
|
|
281
|
+
packDir: args.packDir,
|
|
282
|
+
code: code.value,
|
|
283
|
+
env: packEnv,
|
|
284
|
+
onLog: (line) => emitProgress(` [pack] ${line}`),
|
|
285
|
+
onEvent: (event) => {
|
|
286
|
+
const line = describePackEvent(event);
|
|
287
|
+
if (line !== undefined)
|
|
288
|
+
emitProgress(line);
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
// The front listens for the whole session; children come and go behind
|
|
292
|
+
// it. It keeps the previous session's port, so a runtime started with
|
|
293
|
+
// its URL (`--runtime-url`) keeps reaching it.
|
|
294
|
+
let pack = dev.createPackService({
|
|
295
|
+
...packOptions,
|
|
296
|
+
...(persisted.packServicePort !== undefined && { port: persisted.packServicePort }),
|
|
297
|
+
...(persisted.packServiceToken !== undefined && { token: persisted.packServiceToken }),
|
|
298
|
+
});
|
|
299
|
+
let packFront;
|
|
300
|
+
try {
|
|
301
|
+
packFront = await pack.listen();
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
if (persisted.packServicePort === undefined)
|
|
305
|
+
throw err;
|
|
306
|
+
emitProgress(` the pack service's port ${persisted.packServicePort} is taken: using another one${args.runtimeUrl !== undefined ? ' (restart your runtime with the new runtime.env)' : ''}`);
|
|
307
|
+
await pack.close().catch(() => undefined);
|
|
308
|
+
pack = dev.createPackService(packOptions);
|
|
309
|
+
packFront = await pack.listen();
|
|
310
|
+
}
|
|
311
|
+
const builder = dev.createPackBuilder({
|
|
312
|
+
packDir: args.packDir,
|
|
313
|
+
patterns: projectEnv.discoveryPatterns,
|
|
314
|
+
code: code.value,
|
|
315
|
+
env: packEnv,
|
|
316
|
+
});
|
|
317
|
+
const refresher = createPackRefresher({
|
|
318
|
+
dev,
|
|
319
|
+
pack,
|
|
320
|
+
builder,
|
|
321
|
+
packDir: args.packDir,
|
|
322
|
+
env: packEnv,
|
|
323
|
+
code: code.value,
|
|
324
|
+
});
|
|
325
|
+
// Bundle, index and start the pack's code before the runtime: it reads
|
|
326
|
+
// that index, and calls that pack service, from its first request.
|
|
327
|
+
emitProgress('Bundling + indexing the pack...');
|
|
328
|
+
const bootIndex = await refresher.refresh();
|
|
329
|
+
emitProgress(args.runtimeUrl !== undefined
|
|
330
|
+
? `Using the Kindgi runtime at ${args.runtimeUrl}...`
|
|
331
|
+
: `Starting the Kindgi runtime (${args.runtimeImage})...`);
|
|
332
|
+
let server;
|
|
333
|
+
try {
|
|
334
|
+
server = await withHeartbeat('still starting the runtime', 3000, () => dev.startApiServer({
|
|
335
|
+
port: args.port,
|
|
336
|
+
databaseUrl,
|
|
337
|
+
tenantId: effectiveTenantId,
|
|
338
|
+
token: effectiveToken,
|
|
339
|
+
userId: effectiveUserId,
|
|
340
|
+
packDir: args.packDir,
|
|
341
|
+
...(projectEnv.localEnvFiles !== undefined && {
|
|
342
|
+
localEnvFiles: projectEnv.localEnvFiles,
|
|
343
|
+
}),
|
|
344
|
+
hostEnv: ctx.env,
|
|
345
|
+
packService: { url: packFront.url, token: pack.token },
|
|
346
|
+
...(publicRunTokens.keyPath !== undefined && {
|
|
347
|
+
publicRunTokenKeyPath: publicRunTokens.keyPath,
|
|
348
|
+
}),
|
|
349
|
+
...(publicRunTokens.corsOrigins.length > 0 && {
|
|
350
|
+
corsOrigins: publicRunTokens.corsOrigins,
|
|
351
|
+
}),
|
|
352
|
+
runtimeImage: args.runtimeImage,
|
|
353
|
+
...(args.runtimeUrl !== undefined && { runtimeUrl: args.runtimeUrl }),
|
|
354
|
+
onLog: (line) => emitProgress(` [runtime] ${line}`),
|
|
355
|
+
onProgress: emitProgress,
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
await pack.close().catch(() => undefined);
|
|
360
|
+
return {
|
|
361
|
+
kind: 'error',
|
|
362
|
+
stderr: `kindgi dev couldn't start the Kindgi runtime: ${err.message}\n`,
|
|
363
|
+
exitCode: 1,
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
emitProgress(`✓ the Kindgi runtime is serving at ${server.baseUrl}`);
|
|
367
|
+
const client = ctx.clientFor(server.baseUrl, server.token);
|
|
368
|
+
// Boot-time register, from the index built above.
|
|
369
|
+
emitProgress('Registering primitives...');
|
|
370
|
+
const bootReport = bootIndex.kind === 'ok'
|
|
371
|
+
? await registerFromIndex({
|
|
372
|
+
index: bootIndex.index,
|
|
373
|
+
apiUrl: server.baseUrl,
|
|
374
|
+
token: server.token,
|
|
375
|
+
client,
|
|
376
|
+
fetchImpl: ctx.fetch,
|
|
377
|
+
projectId: server.defaultProjectId,
|
|
378
|
+
packDir: args.packDir,
|
|
379
|
+
})
|
|
380
|
+
: emptyReport();
|
|
381
|
+
if (bootIndex.kind === 'ok') {
|
|
382
|
+
const c = bootIndex.counts;
|
|
383
|
+
emitProgress(`✓ loaded: ${c.tools} tools, ${c.guardrails} guardrails, ${c.agents} agents, ${c.flows} flows`);
|
|
384
|
+
for (const e of bootIndex.fileErrors) {
|
|
385
|
+
emitProgress(` ⚠ indexer: ${e.filePath ?? '?'} [${e.code}] ${e.message}`);
|
|
386
|
+
}
|
|
387
|
+
for (const f of bootReport.failed) {
|
|
388
|
+
emitProgress(` ⚠ ${f.kind}: ${f.id} — ${f.message ?? 'unknown reason'}`);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// Hints run the project's own kindgi through its package manager — or,
|
|
392
|
+
// for a Python pack (no npm project), the kindgi on PATH.
|
|
393
|
+
const runner = await detectBinRunner(args.packDir, code.value.language);
|
|
394
|
+
const kindgi = (...a) => binDisplay(runner, 'kindgi', a);
|
|
395
|
+
const providers = await registeredProviders(client);
|
|
396
|
+
const registerProviderCommand = kindgi('providers', 'register', '--preset=anthropic');
|
|
397
|
+
const bannerLines = renderDevBanner({
|
|
398
|
+
baseUrl: server.baseUrl,
|
|
399
|
+
tenantId: server.tenantId,
|
|
400
|
+
token: server.token,
|
|
401
|
+
providers,
|
|
402
|
+
registerProviderCommand,
|
|
403
|
+
watching: args.watch,
|
|
404
|
+
packDir: args.packDir,
|
|
405
|
+
bootIndex,
|
|
406
|
+
bootReport,
|
|
407
|
+
discoveryRoots: discoveryRoots(projectEnv.discoveryPatterns),
|
|
408
|
+
...(servicesHandle !== undefined && { autoStartedServices: servicesHandle.services }),
|
|
409
|
+
corsOrigins: publicRunTokens.corsOrigins,
|
|
410
|
+
});
|
|
411
|
+
// Write `.kindgirc.json` to the pack root so a SECOND terminal in
|
|
412
|
+
// the same directory (`cd <pack>; kindgi runs start ...`) picks up
|
|
413
|
+
// the api URL + bearer token automatically. Same file the config
|
|
414
|
+
// precedence in `config.ts` already reads. PERSISTS across boots by
|
|
415
|
+
// default — token + tenantId from the previous boot were read +
|
|
416
|
+
// reused above, so subsequent writes here are idempotent. The
|
|
417
|
+
// apiUrl gets refreshed each boot in case the port changed (--port
|
|
418
|
+
// flag, or the future auto-select on collision). Scaffolded
|
|
419
|
+
// .gitignore excludes it so it never ends up in git. Delete via
|
|
420
|
+
// `kindgi dev --reset` to force a fresh token + tenantId on the
|
|
421
|
+
// next boot.
|
|
422
|
+
//
|
|
423
|
+
// `tenantId` persistence is load-bearing after the r1
|
|
424
|
+
// named-volumes Postgres fix: without it, every boot creates a new
|
|
425
|
+
// tenant row and orphans the pack's registered primitives under old
|
|
426
|
+
// tenant UUIDs. Persisting keeps the "run this pack" identity
|
|
427
|
+
// stable across restarts.
|
|
428
|
+
let kindgircWritten = false;
|
|
429
|
+
try {
|
|
430
|
+
await writeFile(kindgircPath, `${JSON.stringify({
|
|
431
|
+
apiUrl: server.baseUrl,
|
|
432
|
+
token: server.token,
|
|
433
|
+
tenantId: server.tenantId,
|
|
434
|
+
userId: server.userId,
|
|
435
|
+
packServicePort: packFront.port,
|
|
436
|
+
packServiceToken: pack.token,
|
|
437
|
+
}, null, 2)}\n`,
|
|
438
|
+
// It holds the dev token and the pack service's token.
|
|
439
|
+
{ encoding: 'utf8', mode: 0o600 });
|
|
440
|
+
await chmod(kindgircPath, 0o600);
|
|
441
|
+
kindgircWritten = true;
|
|
442
|
+
}
|
|
443
|
+
catch {
|
|
444
|
+
// Best effort — if the pack dir is read-only or something goes wrong,
|
|
445
|
+
// don't fail boot. The user can still pass --url/--token flags.
|
|
446
|
+
}
|
|
447
|
+
// Emit the "you're up, here's what to do next" block live to stderr
|
|
448
|
+
// so watch-mode users see it immediately instead of after Ctrl-C.
|
|
449
|
+
// (The returned command result also includes bannerLines; in watch
|
|
450
|
+
// mode that flushes on shutdown, in --no-watch it flushes on exit.
|
|
451
|
+
// Duplication in --no-watch is a small cost for watch-mode
|
|
452
|
+
// responsiveness.)
|
|
453
|
+
emitProgress('');
|
|
454
|
+
emitProgress('════════════════════════════════════════');
|
|
455
|
+
emitProgress(' ✓ Kindgi is up');
|
|
456
|
+
emitProgress('════════════════════════════════════════');
|
|
457
|
+
emitProgress(` API ${server.baseUrl}`);
|
|
458
|
+
if (server.consoleMounted === true) {
|
|
459
|
+
emitProgress(` Console ${server.baseUrl}/console/`);
|
|
460
|
+
}
|
|
461
|
+
emitProgress(` Tenant ${server.tenantId}`);
|
|
462
|
+
emitProgress(` Token ${server.token}`);
|
|
463
|
+
if (providers !== undefined) {
|
|
464
|
+
emitProgress(` Providers ${describeProviders(providers, registerProviderCommand)}`);
|
|
465
|
+
}
|
|
466
|
+
emitProgress(` Origins ${describeBrowserOrigins(publicRunTokens.corsOrigins)}`);
|
|
467
|
+
emitProgress('');
|
|
468
|
+
if (bootIndex.kind === 'ok' && bootIndex.counts.agents > 0) {
|
|
469
|
+
if (kindgircWritten) {
|
|
470
|
+
emitProgress(' Try it (from another terminal, cd to the pack dir first):');
|
|
471
|
+
}
|
|
472
|
+
else {
|
|
473
|
+
emitProgress(' Try it (auto-config write failed — pass --url + --token flags):');
|
|
474
|
+
}
|
|
475
|
+
// Suggest the first agent id from the index for a concrete
|
|
476
|
+
// starting-point command.
|
|
477
|
+
const firstAgent = bootIndex.index
|
|
478
|
+
?.agents?.[0]?.id;
|
|
479
|
+
if (firstAgent !== undefined) {
|
|
480
|
+
emitProgress(` ${kindgi('runs', 'start', `--agent=${firstAgent}`, `--input='{"userMessage":"hi"}'`)}`);
|
|
481
|
+
emitProgress(` ${kindgi('runs', 'stream', '<run-id>')}`);
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
emitProgress(` ${kindgi('runs', 'start', '--agent=<agent-id>', `--input='{"userMessage":"hi"}'`)}`);
|
|
485
|
+
}
|
|
486
|
+
emitProgress('');
|
|
487
|
+
}
|
|
488
|
+
emitProgress(' Hit a framework rough edge?');
|
|
489
|
+
emitProgress(` echo "…" | ${kindgi('feedback', 'write', '--kind=bug', '--title="…"', '--body-stdin')} (appends to FEEDBACK.md)`);
|
|
490
|
+
emitProgress('');
|
|
491
|
+
await emitSkillDriftHint(args.packDir, code.value.language, kindgi('skills', 'sync'));
|
|
492
|
+
if (args.watch) {
|
|
493
|
+
emitProgress(` Watching ${args.packDir}`);
|
|
494
|
+
emitProgress(' Ctrl+C to stop.');
|
|
495
|
+
emitProgress('');
|
|
496
|
+
}
|
|
497
|
+
const watchHandles = [];
|
|
498
|
+
let watchTicks = 0;
|
|
499
|
+
let lastWatchOutcome;
|
|
500
|
+
let lastWatchReport;
|
|
501
|
+
// Refresh ticks still running. Shutdown drains these so a tick never
|
|
502
|
+
// registers against a server that is already shutting down.
|
|
503
|
+
const inFlightTicks = new Set();
|
|
504
|
+
// Refreshes asked for while one runs collapse into one; report it once.
|
|
505
|
+
const reported = new WeakSet();
|
|
506
|
+
if (args.watch) {
|
|
507
|
+
const onRefresh = (build) => {
|
|
508
|
+
// The refresh runs asynchronously so no watcher ever blocks. Any
|
|
509
|
+
// failure lands in `lastWatchOutcome` / stderr; the server stays up.
|
|
510
|
+
const refreshed = refresher.refresh(build);
|
|
511
|
+
if (reported.has(refreshed))
|
|
512
|
+
return;
|
|
513
|
+
reported.add(refreshed);
|
|
514
|
+
const tick = (async () => {
|
|
515
|
+
watchTicks += 1;
|
|
516
|
+
const startedAt = Date.now();
|
|
517
|
+
const outcome = await refreshed;
|
|
518
|
+
lastWatchOutcome = outcome;
|
|
519
|
+
if (outcome.kind === 'ok') {
|
|
520
|
+
const report = await registerFromIndex({
|
|
521
|
+
index: outcome.index,
|
|
522
|
+
apiUrl: server.baseUrl,
|
|
523
|
+
token: server.token,
|
|
524
|
+
client,
|
|
525
|
+
fetchImpl: ctx.fetch,
|
|
526
|
+
projectId: server.defaultProjectId,
|
|
527
|
+
packDir: args.packDir,
|
|
528
|
+
});
|
|
529
|
+
lastWatchReport = report;
|
|
530
|
+
emitWatchTick({
|
|
531
|
+
elapsedMs: Date.now() - startedAt,
|
|
532
|
+
counts: outcome.counts,
|
|
533
|
+
fileErrors: outcome.fileErrors,
|
|
534
|
+
registered: report.registered.length,
|
|
535
|
+
failed: report.failed,
|
|
536
|
+
});
|
|
537
|
+
}
|
|
538
|
+
else if (outcome.code === 'discovery-empty') {
|
|
539
|
+
lastWatchReport = undefined;
|
|
540
|
+
emitProgress(' (no primitives yet)');
|
|
541
|
+
}
|
|
542
|
+
else {
|
|
543
|
+
lastWatchReport = undefined;
|
|
544
|
+
emitProgress(` ✗ refresh failed [${outcome.code}] ${outcome.message}`);
|
|
545
|
+
}
|
|
546
|
+
})();
|
|
547
|
+
inFlightTicks.add(tick);
|
|
548
|
+
void tick.finally(() => inFlightTicks.delete(tick));
|
|
549
|
+
};
|
|
550
|
+
// Code: esbuild rebuilds when any file a bundle read changes —
|
|
551
|
+
// shared libraries outside the discovery folders included.
|
|
552
|
+
await builder.watch((build) => onRefresh(build));
|
|
553
|
+
// A primitive file added or removed changes the entry points.
|
|
554
|
+
watchHandles.push(await dev.watchPack(args.packDir, () => {
|
|
555
|
+
void builder.syncEntries();
|
|
556
|
+
}, { debounceMs: DEFAULT_WATCH_DEBOUNCE_MS, patterns: projectEnv.discoveryPatterns }));
|
|
557
|
+
// The env files: the same code, restarted with the new environment.
|
|
558
|
+
watchHandles.push(await dev.watchPack(args.packDir, () => onRefresh(), {
|
|
559
|
+
debounceMs: DEFAULT_WATCH_DEBOUNCE_MS,
|
|
560
|
+
patterns: [],
|
|
561
|
+
files: devPackEnvFiles(args.packDir, projectEnv.localEnvFiles),
|
|
562
|
+
}));
|
|
563
|
+
}
|
|
564
|
+
// Watch mode: wait for the caller's abort signal (SIGINT/SIGTERM in
|
|
565
|
+
// production; injected AbortController.signal in tests). Non-watch
|
|
566
|
+
// mode: return immediately after boot registration.
|
|
567
|
+
if (args.watch) {
|
|
568
|
+
await waitForAbort(ctx.stopSignal);
|
|
569
|
+
emitProgress('Shutting down...');
|
|
570
|
+
for (const handle of watchHandles)
|
|
571
|
+
await handle.close();
|
|
572
|
+
}
|
|
573
|
+
// Nothing starts a refresh any more; drain the ones in flight.
|
|
574
|
+
await builder.dispose();
|
|
575
|
+
await Promise.allSettled(inFlightTicks);
|
|
576
|
+
await refresher.idle();
|
|
577
|
+
await server.shutdown();
|
|
578
|
+
await pack.close();
|
|
579
|
+
// `.kindgirc.json` is intentionally NOT deleted on exit — the
|
|
580
|
+
// persisted token survives so re-boots and second-terminal
|
|
581
|
+
// sessions keep working with the same bearer. `kindgi dev --reset`
|
|
582
|
+
// deletes it if you want a fresh token.
|
|
583
|
+
void kindgircWritten;
|
|
584
|
+
// The bundled services keep running: other `kindgi dev` sessions on
|
|
585
|
+
// this machine share them (see `StartedServicesHandle`).
|
|
586
|
+
const summary = {
|
|
587
|
+
apiUrl: server.baseUrl,
|
|
588
|
+
tenantId: server.tenantId,
|
|
589
|
+
token: server.token,
|
|
590
|
+
port: server.port,
|
|
591
|
+
watch: args.watch,
|
|
592
|
+
packDir: args.packDir,
|
|
593
|
+
boot: summariseOutcome(bootIndex, bootReport),
|
|
594
|
+
...(args.watch && {
|
|
595
|
+
watchTicks,
|
|
596
|
+
...(lastWatchOutcome !== undefined && {
|
|
597
|
+
lastWatch: summariseOutcome(lastWatchOutcome, lastWatchReport ?? emptyReport()),
|
|
598
|
+
}),
|
|
599
|
+
}),
|
|
600
|
+
};
|
|
601
|
+
const rendered = renderJson(summary, ctx.globals.format);
|
|
602
|
+
return {
|
|
603
|
+
kind: 'ok',
|
|
604
|
+
rendered: { stdout: rendered.stdout, stderr: `${bannerLines.join('\n')}\n` },
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Public run tokens for the browser app under development: signed with
|
|
609
|
+
* the key file when `KINDGI_PUBLIC_TOKEN_SIGNING_KEY_PATH` is set,
|
|
610
|
+
* otherwise with a key made now (tokens stop working when `kindgi dev`
|
|
611
|
+
* stops). `KINDGI_CORS_ORIGINS` lists the app's origins
|
|
612
|
+
* (`http://localhost:3000`). Both come from the shell, then the env files,
|
|
613
|
+
* like `KINDGI_DATABASE_URL`.
|
|
614
|
+
*/
|
|
615
|
+
async function resolveDevPublicRunTokens(shell, files) {
|
|
616
|
+
const pick = (name) => {
|
|
617
|
+
const value = shell[name] ?? files[name];
|
|
618
|
+
return value === undefined || value.trim() === '' ? undefined : value.trim();
|
|
619
|
+
};
|
|
620
|
+
try {
|
|
621
|
+
const keyPath = pick(PUBLIC_TOKEN_KEY_PATH_VAR);
|
|
622
|
+
if (keyPath !== undefined && (!isAbsolute(keyPath) || !existsSync(keyPath))) {
|
|
623
|
+
throw new Error(`${PUBLIC_TOKEN_KEY_PATH_VAR} must be the absolute path of an existing key file. Got "${keyPath}".`);
|
|
624
|
+
}
|
|
625
|
+
return { kind: 'ok', keyPath, corsOrigins: parseCorsOrigins(pick(CORS_ORIGINS_VAR)) };
|
|
626
|
+
}
|
|
627
|
+
catch (err) {
|
|
628
|
+
return { kind: 'error', stderr: `kindgi dev: ${err.message}\n`, exitCode: 1 };
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
/**
|
|
632
|
+
* Read the project's env files for `local` and report them in the boot
|
|
633
|
+
* log (names and counts only — never values).
|
|
634
|
+
*/
|
|
635
|
+
async function loadDevProjectEnv(ctx, packDir) {
|
|
636
|
+
const fail = (message) => ({
|
|
637
|
+
kind: 'error',
|
|
638
|
+
stderr: `kindgi dev: ${message}\n`,
|
|
639
|
+
exitCode: 1,
|
|
640
|
+
});
|
|
641
|
+
const settings = await loadLocalEnvSettings(ctx, packDir);
|
|
642
|
+
if (settings.kind === 'error')
|
|
643
|
+
return fail(settings.message);
|
|
644
|
+
if (settings.configProblem !== undefined) {
|
|
645
|
+
return fail(`could not load kindgi.config.ts at ${packDir}: ${settings.configProblem}`);
|
|
646
|
+
}
|
|
647
|
+
let env;
|
|
648
|
+
try {
|
|
649
|
+
env = await readPackEnv({
|
|
650
|
+
packDir,
|
|
651
|
+
envName: LOCAL_ENV_NAME,
|
|
652
|
+
...(settings.localEnvFiles !== undefined && { localEnvFiles: settings.localEnvFiles }),
|
|
653
|
+
env: ctx.env,
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
catch (err) {
|
|
657
|
+
return fail(`could not read the env files: ${err.message}`);
|
|
658
|
+
}
|
|
659
|
+
const label = (paths) => paths.map((p) => displayEnvPath(packDir, p)).join(', ');
|
|
660
|
+
emitProgress(env.present.length === 0
|
|
661
|
+
? `env files: none yet (reads ${label(env.files.read)})`
|
|
662
|
+
: `✓ env files: ${label(env.present)} — ${Object.keys(packValues(env.values)).length} name(s) for the pack`);
|
|
663
|
+
for (const line of describeEnvDiagnostics(packDir, env.diagnostics))
|
|
664
|
+
emitProgress(` ! ${line}`);
|
|
665
|
+
const discovery = settings.config?.discovery;
|
|
666
|
+
const language = settings.config !== undefined ? packLanguage(settings.config) : 'node';
|
|
667
|
+
return {
|
|
668
|
+
kind: 'ok',
|
|
669
|
+
localEnvFiles: settings.localEnvFiles,
|
|
670
|
+
runtime: runtimeValues(env.values),
|
|
671
|
+
discoveryPatterns: Object.values(resolveDiscovery(typeof discovery === 'object' && discovery !== null
|
|
672
|
+
? discovery
|
|
673
|
+
: undefined, language)),
|
|
674
|
+
language,
|
|
675
|
+
config: settings.config,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
function resolveDevArgs(ctx) {
|
|
679
|
+
// --port
|
|
680
|
+
const portRaw = ctx.options.port;
|
|
681
|
+
let port = DEFAULT_DEV_PORT;
|
|
682
|
+
if (typeof portRaw === 'string' && portRaw !== '') {
|
|
683
|
+
const parsedPort = Number.parseInt(portRaw, 10);
|
|
684
|
+
if (!Number.isFinite(parsedPort) || parsedPort < 0 || parsedPort > 65535) {
|
|
685
|
+
return {
|
|
686
|
+
kind: 'error',
|
|
687
|
+
stderr: `Invalid --port: ${portRaw}. Expected an integer 0..65535.\n`,
|
|
688
|
+
exitCode: 1,
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
port = parsedPort;
|
|
692
|
+
}
|
|
693
|
+
// --database-url > KINDGI_DATABASE_URL
|
|
694
|
+
const dbFlag = ctx.options['database-url'];
|
|
695
|
+
const dbEnv = getEnv(ctx, 'KINDGI_DATABASE_URL');
|
|
696
|
+
const databaseUrl = typeof dbFlag === 'string' && dbFlag !== ''
|
|
697
|
+
? dbFlag
|
|
698
|
+
: typeof dbEnv === 'string' && dbEnv !== ''
|
|
699
|
+
? dbEnv
|
|
700
|
+
: undefined;
|
|
701
|
+
// databaseUrl is allowed to be undefined at resolve time. `runDev`
|
|
702
|
+
// handles it: if `startServices` is wired AND docker-compose is
|
|
703
|
+
// available, auto-start postgres+openfga+minio and use the
|
|
704
|
+
// container URL. If not, error with the manual quick-start hint.
|
|
705
|
+
// --tenant / --dev-token
|
|
706
|
+
const tenantFlag = ctx.options.tenant;
|
|
707
|
+
const tokenFlag = ctx.options['dev-token'];
|
|
708
|
+
const tenantId = typeof tenantFlag === 'string' && tenantFlag !== '' ? tenantFlag : undefined;
|
|
709
|
+
const token = typeof tokenFlag === 'string' && tokenFlag !== '' ? tokenFlag : undefined;
|
|
710
|
+
// --watch / --no-watch. Defaults ON for `kindgi dev`.
|
|
711
|
+
const watchOn = ctx.options.watch === true;
|
|
712
|
+
const watchOff = ctx.options['no-watch'] === true;
|
|
713
|
+
if (watchOn && watchOff) {
|
|
714
|
+
return {
|
|
715
|
+
kind: 'error',
|
|
716
|
+
stderr: 'Contradictory flags: --watch and --no-watch cannot both be set.\n',
|
|
717
|
+
exitCode: 1,
|
|
718
|
+
};
|
|
719
|
+
}
|
|
720
|
+
const watch = !watchOff;
|
|
721
|
+
// --path — pack root. Default is cwd.
|
|
722
|
+
const pathFlag = ctx.options.path;
|
|
723
|
+
const rawPath = typeof pathFlag === 'string' && pathFlag !== '' ? pathFlag : ctx.cwd;
|
|
724
|
+
const packDir = isAbsolute(rawPath) ? rawPath : resolve(ctx.cwd, rawPath);
|
|
725
|
+
// --reset — a fresh start for this pack only.
|
|
726
|
+
const reset = ctx.options.reset === true;
|
|
727
|
+
const recreateServices = ctx.options['recreate-services'] === true;
|
|
728
|
+
// --runtime-image / --runtime-url — which runtime: an image, or one you run.
|
|
729
|
+
const imageFlag = ctx.options['runtime-image'];
|
|
730
|
+
const urlFlag = ctx.options['runtime-url'];
|
|
731
|
+
const runtimeUrl = typeof urlFlag === 'string' && urlFlag !== '' ? urlFlag : undefined;
|
|
732
|
+
if (runtimeUrl !== undefined && typeof imageFlag === 'string' && imageFlag !== '') {
|
|
733
|
+
return {
|
|
734
|
+
kind: 'error',
|
|
735
|
+
stderr: '--runtime-image and --runtime-url exclude each other: an image, or a runtime you run.\n',
|
|
736
|
+
exitCode: 1,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
if (runtimeUrl !== undefined && !/^https?:\/\/[^/]+\/?$/.test(runtimeUrl)) {
|
|
740
|
+
return {
|
|
741
|
+
kind: 'error',
|
|
742
|
+
stderr: `Invalid --runtime-url: ${runtimeUrl}. Expected the runtime's origin, e.g. http://127.0.0.1:4000.\n`,
|
|
743
|
+
exitCode: 1,
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
const runtimeImage = typeof imageFlag === 'string' && imageFlag !== '' ? imageFlag : DEFAULT_RUNTIME_IMAGE;
|
|
747
|
+
return {
|
|
748
|
+
kind: 'ok',
|
|
749
|
+
args: {
|
|
750
|
+
port,
|
|
751
|
+
databaseUrl,
|
|
752
|
+
tenantId,
|
|
753
|
+
token,
|
|
754
|
+
watch,
|
|
755
|
+
packDir,
|
|
756
|
+
reset,
|
|
757
|
+
recreateServices,
|
|
758
|
+
runtimeImage,
|
|
759
|
+
runtimeUrl,
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
function pickRunners(ctx) {
|
|
764
|
+
if (ctx.devRunners !== undefined)
|
|
765
|
+
return { kind: 'ok', runners: ctx.devRunners };
|
|
766
|
+
return {
|
|
767
|
+
kind: 'error',
|
|
768
|
+
stderr: 'Internal error: kindgi dev requires dev runners to be wired. ' +
|
|
769
|
+
'Rebuild the CLI (`pnpm --filter @kindgi/cli build`).\n',
|
|
770
|
+
exitCode: 1,
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Read the persisted `.kindgirc.json` if present. Returns `undefined`
|
|
775
|
+
* for any missing field — all missing cases fall back to
|
|
776
|
+
* server-generated on this boot. Called at boot so the previous run's
|
|
777
|
+
* token AND tenantId are reused across restarts.
|
|
778
|
+
*
|
|
779
|
+
* Persisting `tenantId` alongside `token` matters after the r1
|
|
780
|
+
* named-volume Postgres change: without it, `seedTenant` generates a
|
|
781
|
+
* fresh UUID + slug on every boot, orphaning the pack's registered
|
|
782
|
+
* primitives from previous boots under old tenant rows in the
|
|
783
|
+
* persisted DB.
|
|
784
|
+
*/
|
|
785
|
+
async function readPersistedRc(kindgircPath) {
|
|
786
|
+
try {
|
|
787
|
+
const raw = await readFile(kindgircPath, 'utf8');
|
|
788
|
+
const parsed = JSON.parse(raw);
|
|
789
|
+
return {
|
|
790
|
+
...(typeof parsed.packServiceToken === 'string' &&
|
|
791
|
+
/^[A-Za-z0-9_-]{32,}$/.test(parsed.packServiceToken) && {
|
|
792
|
+
packServiceToken: parsed.packServiceToken,
|
|
793
|
+
}),
|
|
794
|
+
...(typeof parsed.packServicePort === 'number' &&
|
|
795
|
+
Number.isInteger(parsed.packServicePort) &&
|
|
796
|
+
parsed.packServicePort > 0 &&
|
|
797
|
+
parsed.packServicePort < 65536 && { packServicePort: parsed.packServicePort }),
|
|
798
|
+
...(typeof parsed.token === 'string' && parsed.token.length > 0 && { token: parsed.token }),
|
|
799
|
+
...(typeof parsed.tenantId === 'string' &&
|
|
800
|
+
parsed.tenantId.length > 0 && { tenantId: parsed.tenantId }),
|
|
801
|
+
...(typeof parsed.userId === 'string' &&
|
|
802
|
+
parsed.userId.length > 0 && { userId: parsed.userId }),
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
catch {
|
|
806
|
+
return {};
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
/**
|
|
810
|
+
* Emit a short human-readable line per watch tick so the user sees
|
|
811
|
+
* that a save re-indexed + reloaded the pack. Primitives come from
|
|
812
|
+
* disk, so they aren't "re-registered" per se — the neutral
|
|
813
|
+
* "loaded" verb covers both paths.
|
|
814
|
+
*/
|
|
815
|
+
function emitWatchTick(input) {
|
|
816
|
+
const { counts, fileErrors, failed, elapsedMs, registered } = input;
|
|
817
|
+
const totals = `${counts.tools} tools, ${counts.guardrails} guardrails, ${counts.agents} agents, ${counts.flows} flows`;
|
|
818
|
+
if (fileErrors.length === 0 && failed.length === 0) {
|
|
819
|
+
emitProgress(` ✓ loaded ${registered} primitives (${totals}) in ${elapsedMs}ms`);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
822
|
+
emitProgress(` ⚠ loaded ${registered} of ${registered + failed.length} in ${elapsedMs}ms — ${totals}`);
|
|
823
|
+
for (const e of fileErrors) {
|
|
824
|
+
emitProgress(` ✗ indexer: ${e.filePath ?? '?'} [${e.code}] ${e.message}`);
|
|
825
|
+
}
|
|
826
|
+
for (const f of failed) {
|
|
827
|
+
emitProgress(` ✗ ${f.kind}: ${f.id} — ${f.message ?? 'unknown reason'}`);
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
/**
|
|
831
|
+
* Compare the pack's installed framework skills against the bundled
|
|
832
|
+
* SDK versions; print a one-liner if drift is detected. Silent when
|
|
833
|
+
* everything is up to date. Never fails boot — a drift-check crash
|
|
834
|
+
* (missing skills root, unreadable manifest) is swallowed because
|
|
835
|
+
* the drift check is advisory, not load-bearing.
|
|
836
|
+
*/
|
|
837
|
+
async function emitSkillDriftHint(packDir, language, syncCommand) {
|
|
838
|
+
const skillsRoot = defaultSdkSkillsRoot();
|
|
839
|
+
if (skillsRoot === undefined)
|
|
840
|
+
return;
|
|
841
|
+
try {
|
|
842
|
+
const drift = await detectSkillDrift({ skillsRoot, targetDir: packDir, language });
|
|
843
|
+
const total = drift.missing.length + drift.outdated.length;
|
|
844
|
+
if (total === 0)
|
|
845
|
+
return;
|
|
846
|
+
const parts = [];
|
|
847
|
+
if (drift.outdated.length > 0)
|
|
848
|
+
parts.push(`${drift.outdated.length} out of date`);
|
|
849
|
+
if (drift.missing.length > 0)
|
|
850
|
+
parts.push(`${drift.missing.length} not yet installed`);
|
|
851
|
+
emitProgress(` ⚠ Framework skills: ${parts.join(', ')}.`);
|
|
852
|
+
emitProgress(` Run \`${syncCommand}\` to pull the latest.`);
|
|
853
|
+
emitProgress('');
|
|
854
|
+
}
|
|
855
|
+
catch {
|
|
856
|
+
// Advisory; never block boot on a drift-check failure.
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* How this pack's code runs (`dev/pack-code.ts`). For a Python pack, the
|
|
861
|
+
* interpreter is resolved and checked — with the pack's own environment —
|
|
862
|
+
* before anything boots.
|
|
863
|
+
*/
|
|
864
|
+
async function resolveDevPackCode(dev, language, packDir, config, packEnv) {
|
|
865
|
+
const resolved = await resolvePackCode(language, packDir, config);
|
|
866
|
+
if (resolved.kind === 'err') {
|
|
867
|
+
return { kind: 'error', stderr: `kindgi dev: ${resolved.message}\n`, exitCode: 1 };
|
|
868
|
+
}
|
|
869
|
+
if (resolved.value.language === 'python') {
|
|
870
|
+
const checked = await dev.checkPackPython(resolved.value.python, await packEnv(), packDir);
|
|
871
|
+
if (checked.kind === 'err') {
|
|
872
|
+
return { kind: 'error', stderr: `kindgi dev: ${checked.message}\n`, exitCode: 1 };
|
|
873
|
+
}
|
|
874
|
+
emitProgress(`✓ pack code: ${checked.value}`);
|
|
875
|
+
}
|
|
876
|
+
return { kind: 'ok', value: resolved.value };
|
|
877
|
+
}
|
|
878
|
+
async function ensurePackConfig(packDir) {
|
|
879
|
+
return { ok: (await findKindgiConfig(packDir)) !== undefined };
|
|
880
|
+
}
|
|
881
|
+
function getEnv(ctx, key) {
|
|
882
|
+
// `ctx.env` is the only source: the `kindgi` binary passes
|
|
883
|
+
// `process.env` (src/cli.ts), tests pass an explicit record. No
|
|
884
|
+
// per-key fallback to `process.env` — that made "variable unset"
|
|
885
|
+
// untestable whenever the host shell (or CI) exported it.
|
|
886
|
+
return ctx.env[key];
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* The tenant's providers for the banner; `undefined` when they can't be
|
|
890
|
+
* listed (the banner then leaves the line out).
|
|
891
|
+
*/
|
|
892
|
+
async function registeredProviders(client) {
|
|
893
|
+
try {
|
|
894
|
+
const providers = [];
|
|
895
|
+
let cursor;
|
|
896
|
+
do {
|
|
897
|
+
const page = await client.providers.list({
|
|
898
|
+
limit: 100,
|
|
899
|
+
...(cursor !== undefined && { cursor }),
|
|
900
|
+
});
|
|
901
|
+
providers.push(...page.data);
|
|
902
|
+
cursor = page.hasMore ? page.nextCursor : undefined;
|
|
903
|
+
} while (cursor !== undefined);
|
|
904
|
+
return providers;
|
|
905
|
+
}
|
|
906
|
+
catch {
|
|
907
|
+
return undefined;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
/** The browser origins line: who may follow runs with public run tokens (`KINDGI_CORS_ORIGINS`). */
|
|
911
|
+
function describeBrowserOrigins(origins) {
|
|
912
|
+
return origins.length > 0
|
|
913
|
+
? `${origins.join(', ')} (browsers may follow runs with public run tokens)`
|
|
914
|
+
: 'none (set KINDGI_CORS_ORIGINS so a browser app can follow runs)';
|
|
915
|
+
}
|
|
916
|
+
/**
|
|
917
|
+
* The banner's providers line: each provider with its models, fallbacks
|
|
918
|
+
* marked; when only fallbacks (or nothing) can answer, how to register a
|
|
919
|
+
* real model.
|
|
920
|
+
*/
|
|
921
|
+
function describeProviders(providers, registerProviderCommand) {
|
|
922
|
+
if (providers.length === 0) {
|
|
923
|
+
return `none — agent turns fail until one is registered: ${registerProviderCommand}`;
|
|
924
|
+
}
|
|
925
|
+
const named = providers
|
|
926
|
+
.map((p) => p.fallback === true
|
|
927
|
+
? `${p.id} (fallback)`
|
|
928
|
+
: `${p.id} (${p.models.map((m) => m.name).join(', ')})`)
|
|
929
|
+
.join(' · ');
|
|
930
|
+
if (providers.some((p) => p.fallback !== true))
|
|
931
|
+
return named;
|
|
932
|
+
return `${named} — canned replies; for a real model: ${registerProviderCommand}`;
|
|
933
|
+
}
|
|
934
|
+
/** The banner printed to stderr on boot. */
|
|
935
|
+
export function renderDevBanner(inputs) {
|
|
936
|
+
const lines = [];
|
|
937
|
+
lines.push('');
|
|
938
|
+
lines.push(' Starting Kindgi locally (dev: the runtime in a container, your code on this machine)');
|
|
939
|
+
lines.push(` API server ${inputs.baseUrl}`);
|
|
940
|
+
lines.push(` Tenant ${inputs.tenantId}`);
|
|
941
|
+
lines.push(` Bearer token ${inputs.token}`);
|
|
942
|
+
lines.push(` Pack dir ${inputs.packDir}`);
|
|
943
|
+
if (inputs.providers !== undefined) {
|
|
944
|
+
lines.push(` Providers ${describeProviders(inputs.providers, inputs.registerProviderCommand)}`);
|
|
945
|
+
}
|
|
946
|
+
if (inputs.autoStartedServices !== undefined && inputs.autoStartedServices.length > 0) {
|
|
947
|
+
lines.push(` Auto-started ${inputs.autoStartedServices.join(', ')} (docker, shared by every kindgi dev; left running)`);
|
|
948
|
+
}
|
|
949
|
+
if (inputs.corsOrigins !== undefined) {
|
|
950
|
+
lines.push(` Browser origins ${describeBrowserOrigins(inputs.corsOrigins)}`);
|
|
951
|
+
}
|
|
952
|
+
lines.push('');
|
|
953
|
+
lines.push(...renderIndexSection(inputs));
|
|
954
|
+
lines.push('');
|
|
955
|
+
if (inputs.watching) {
|
|
956
|
+
const roots = (inputs.discoveryRoots ?? []).map((r) => (r === '' ? '**' : `${r}/**`));
|
|
957
|
+
lines.push(` Watching ${roots.join(', ')} under ${inputs.packDir}`);
|
|
958
|
+
lines.push(` Ready. Call the API at ${inputs.baseUrl} with Authorization: Bearer <token above>.`);
|
|
959
|
+
lines.push(' Ctrl+C to stop.');
|
|
960
|
+
}
|
|
961
|
+
else {
|
|
962
|
+
lines.push(' --no-watch: single boot + register cycle complete.');
|
|
963
|
+
}
|
|
964
|
+
return lines;
|
|
965
|
+
}
|
|
966
|
+
/** The "what got indexed" part of the banner: counts + failures, an empty pack, or the indexer error. */
|
|
967
|
+
function renderIndexSection(inputs) {
|
|
968
|
+
const index = inputs.bootIndex;
|
|
969
|
+
if (index.kind === 'ok') {
|
|
970
|
+
const c = index.counts;
|
|
971
|
+
const lines = [
|
|
972
|
+
` Discovering pack (${index.packId}@${index.packVersion})`,
|
|
973
|
+
` ✓ ${c.tools} tools ✓ ${c.guardrails} guardrails ✓ ${c.agents} agents ✓ ${c.flows} flows`,
|
|
974
|
+
];
|
|
975
|
+
if (inputs.bootReport.failed.length > 0) {
|
|
976
|
+
lines.push('', ' Registration failures:');
|
|
977
|
+
for (const f of inputs.bootReport.failed) {
|
|
978
|
+
lines.push(` ✗ ${f.kind} ${f.id} — ${f.message ?? 'unknown error'}`);
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
return lines;
|
|
982
|
+
}
|
|
983
|
+
if (index.code === 'discovery-empty') {
|
|
984
|
+
const where = (inputs.discoveryRoots ?? []).map((r) => `${r === '' ? '.' : r}/`).join(', ');
|
|
985
|
+
return [
|
|
986
|
+
` No primitives yet — add a tool or agent${where === '' ? '' : ` under ${where}`}; it registers on save.`,
|
|
987
|
+
];
|
|
988
|
+
}
|
|
989
|
+
return [
|
|
990
|
+
` Indexer failed: [${index.code}] ${index.message}`,
|
|
991
|
+
...(index.filePath !== undefined ? [` at ${index.filePath}`] : []),
|
|
992
|
+
' Fix the pack source; kindgi dev keeps the server up regardless.',
|
|
993
|
+
];
|
|
994
|
+
}
|
|
995
|
+
function summariseOutcome(outcome, report) {
|
|
996
|
+
if (outcome.kind === 'err') {
|
|
997
|
+
return {
|
|
998
|
+
ok: false,
|
|
999
|
+
indexerError: {
|
|
1000
|
+
code: outcome.code,
|
|
1001
|
+
message: outcome.message,
|
|
1002
|
+
...(outcome.filePath !== undefined && { filePath: outcome.filePath }),
|
|
1003
|
+
},
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
return {
|
|
1007
|
+
ok: true,
|
|
1008
|
+
packId: outcome.packId,
|
|
1009
|
+
packVersion: outcome.packVersion,
|
|
1010
|
+
counts: outcome.counts,
|
|
1011
|
+
registered: report.registered.length,
|
|
1012
|
+
failed: report.failed.map((f) => ({ kind: f.kind, id: f.id, message: f.message ?? null })),
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
function emptyReport() {
|
|
1016
|
+
return { registered: [], failed: [] };
|
|
1017
|
+
}
|
|
1018
|
+
function waitForAbort(signal) {
|
|
1019
|
+
// biome-ignore lint/suspicious/noEmptyBlockStatements: wait forever when no signal
|
|
1020
|
+
if (signal === undefined)
|
|
1021
|
+
return new Promise(() => { });
|
|
1022
|
+
if (signal.aborted)
|
|
1023
|
+
return Promise.resolve();
|
|
1024
|
+
return new Promise((resolvePromise) => {
|
|
1025
|
+
signal.addEventListener('abort', () => resolvePromise(), { once: true });
|
|
1026
|
+
});
|
|
1027
|
+
}
|
|
1028
|
+
/** A dev token in the runtime's own format: `kgi_bt_` and 32 random bytes, URL-safe base64. */
|
|
1029
|
+
function generateDevToken() {
|
|
1030
|
+
return `kgi_bt_${randomBytes(32).toString('base64url')}`;
|
|
1031
|
+
}
|
|
1032
|
+
//# sourceMappingURL=dev.js.map
|