@cyanautomation/kaseki-agent 1.4.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/.dockerignore +54 -0
- package/.eslintignore +11 -0
- package/.eslintrc.json +95 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +53 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +53 -0
- package/.github/ISSUE_TEMPLATE/security.md +51 -0
- package/.github/PULL_REQUEST_TEMPLATE/default.md +71 -0
- package/.github/dependabot.yml +38 -0
- package/.github/skills/dependency-cache-optimization/SKILL.md +526 -0
- package/.github/skills/docker-image-management/SKILL.md +532 -0
- package/.github/skills/frontend-design/SKILL.md +782 -0
- package/.github/skills/prompt-engineering/SKILL.md +360 -0
- package/.github/skills/quality-gate-config/SKILL.md +591 -0
- package/.github/skills/result-report-analysis/SKILL.md +576 -0
- package/.github/skills/test-automation/SKILL.md +593 -0
- package/.github/skills/workflow-diagnosis/SKILL.md +468 -0
- package/.github/workflows/build-docker-image.yml +453 -0
- package/.github/workflows/release.yml +68 -0
- package/.releaserc.json +135 -0
- package/CHANGELOG.md +117 -0
- package/CLAUDE.md +336 -0
- package/CONTRIBUTING.md +339 -0
- package/Dockerfile +217 -0
- package/README.md +1527 -0
- package/STYLE.md +521 -0
- package/add-js-extensions.d.ts +9 -0
- package/add-js-extensions.d.ts.map +1 -0
- package/add-js-extensions.js.map +1 -0
- package/dist/add-js-extensions.d.ts +9 -0
- package/dist/add-js-extensions.d.ts.map +1 -0
- package/dist/add-js-extensions.js +52 -0
- package/dist/add-js-extensions.js.map +1 -0
- package/dist/ansi-colors.d.ts +26 -0
- package/dist/ansi-colors.d.ts.map +1 -0
- package/dist/ansi-colors.js +51 -0
- package/dist/ansi-colors.js.map +1 -0
- package/dist/cli/BaseCommand.d.ts +18 -0
- package/dist/cli/BaseCommand.d.ts.map +1 -0
- package/dist/cli/BaseCommand.js +31 -0
- package/dist/cli/BaseCommand.js.map +1 -0
- package/dist/cli/KasekiCLI.d.ts +30 -0
- package/dist/cli/KasekiCLI.d.ts.map +1 -0
- package/dist/cli/KasekiCLI.js +134 -0
- package/dist/cli/KasekiCLI.js.map +1 -0
- package/dist/cli/commands/ConfigCommand.d.ts +13 -0
- package/dist/cli/commands/ConfigCommand.d.ts.map +1 -0
- package/dist/cli/commands/ConfigCommand.js +131 -0
- package/dist/cli/commands/ConfigCommand.js.map +1 -0
- package/dist/cli/commands/DoctorCommand.d.ts +45 -0
- package/dist/cli/commands/DoctorCommand.d.ts.map +1 -0
- package/dist/cli/commands/DoctorCommand.js +309 -0
- package/dist/cli/commands/DoctorCommand.js.map +1 -0
- package/dist/cli/commands/ListCommand.d.ts +9 -0
- package/dist/cli/commands/ListCommand.d.ts.map +1 -0
- package/dist/cli/commands/ListCommand.js +81 -0
- package/dist/cli/commands/ListCommand.js.map +1 -0
- package/dist/cli/commands/ReportCommand.d.ts +9 -0
- package/dist/cli/commands/ReportCommand.d.ts.map +1 -0
- package/dist/cli/commands/ReportCommand.js +98 -0
- package/dist/cli/commands/ReportCommand.js.map +1 -0
- package/dist/cli/commands/RunCommand.d.ts +13 -0
- package/dist/cli/commands/RunCommand.d.ts.map +1 -0
- package/dist/cli/commands/RunCommand.js +191 -0
- package/dist/cli/commands/RunCommand.js.map +1 -0
- package/dist/cli/commands/SecretsCommand.d.ts +9 -0
- package/dist/cli/commands/SecretsCommand.d.ts.map +1 -0
- package/dist/cli/commands/SecretsCommand.js +109 -0
- package/dist/cli/commands/SecretsCommand.js.map +1 -0
- package/dist/cli/commands/ServeCommand.d.ts +9 -0
- package/dist/cli/commands/ServeCommand.d.ts.map +1 -0
- package/dist/cli/commands/ServeCommand.js +50 -0
- package/dist/cli/commands/ServeCommand.js.map +1 -0
- package/dist/cli/commands/SetupCommand.d.ts +42 -0
- package/dist/cli/commands/SetupCommand.d.ts.map +1 -0
- package/dist/cli/commands/SetupCommand.js +249 -0
- package/dist/cli/commands/SetupCommand.js.map +1 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +130 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/ConfigManager.d.ts +395 -0
- package/dist/config/ConfigManager.d.ts.map +1 -0
- package/dist/config/ConfigManager.js +446 -0
- package/dist/config/ConfigManager.js.map +1 -0
- package/dist/docker/DockerManager.d.ts +69 -0
- package/dist/docker/DockerManager.d.ts.map +1 -0
- package/dist/docker/DockerManager.js +266 -0
- package/dist/docker/DockerManager.js.map +1 -0
- package/dist/event-aggregator.d.ts +71 -0
- package/dist/event-aggregator.d.ts.map +1 -0
- package/dist/event-aggregator.js +95 -0
- package/dist/event-aggregator.js.map +1 -0
- package/dist/github-app-token.d.ts +16 -0
- package/dist/github-app-token.d.ts.map +1 -0
- package/dist/github-app-token.js +148 -0
- package/dist/github-app-token.js.map +1 -0
- package/dist/idempotency-store.d.ts +61 -0
- package/dist/idempotency-store.d.ts.map +1 -0
- package/dist/idempotency-store.js +321 -0
- package/dist/idempotency-store.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/instance/InstanceManager.d.ts +81 -0
- package/dist/instance/InstanceManager.d.ts.map +1 -0
- package/dist/instance/InstanceManager.js +220 -0
- package/dist/instance/InstanceManager.js.map +1 -0
- package/dist/instance-metadata-reader.d.ts +48 -0
- package/dist/instance-metadata-reader.d.ts.map +1 -0
- package/dist/instance-metadata-reader.js +94 -0
- package/dist/instance-metadata-reader.js.map +1 -0
- package/dist/instance-state-derivation.d.ts +42 -0
- package/dist/instance-state-derivation.d.ts.map +1 -0
- package/dist/instance-state-derivation.js +133 -0
- package/dist/instance-state-derivation.js.map +1 -0
- package/dist/job-scheduler.d.ts +124 -0
- package/dist/job-scheduler.d.ts.map +1 -0
- package/dist/job-scheduler.js +992 -0
- package/dist/job-scheduler.js.map +1 -0
- package/dist/kaseki-api-client.d.ts +89 -0
- package/dist/kaseki-api-client.d.ts.map +1 -0
- package/dist/kaseki-api-client.js +405 -0
- package/dist/kaseki-api-client.js.map +1 -0
- package/dist/kaseki-api-config.d.ts +34 -0
- package/dist/kaseki-api-config.d.ts.map +1 -0
- package/dist/kaseki-api-config.js +113 -0
- package/dist/kaseki-api-config.js.map +1 -0
- package/dist/kaseki-api-routes.d.ts +13 -0
- package/dist/kaseki-api-routes.d.ts.map +1 -0
- package/dist/kaseki-api-routes.js +559 -0
- package/dist/kaseki-api-routes.js.map +1 -0
- package/dist/kaseki-api-service-wrapper.d.ts +43 -0
- package/dist/kaseki-api-service-wrapper.d.ts.map +1 -0
- package/dist/kaseki-api-service-wrapper.js +150 -0
- package/dist/kaseki-api-service-wrapper.js.map +1 -0
- package/dist/kaseki-api-service.d.ts +16 -0
- package/dist/kaseki-api-service.d.ts.map +1 -0
- package/dist/kaseki-api-service.js +143 -0
- package/dist/kaseki-api-service.js.map +1 -0
- package/dist/kaseki-api-types.d.ts +440 -0
- package/dist/kaseki-api-types.d.ts.map +1 -0
- package/dist/kaseki-api-types.js +64 -0
- package/dist/kaseki-api-types.js.map +1 -0
- package/dist/kaseki-cli-lib.d.ts +219 -0
- package/dist/kaseki-cli-lib.d.ts.map +1 -0
- package/dist/kaseki-cli-lib.js +523 -0
- package/dist/kaseki-cli-lib.js.map +1 -0
- package/dist/kaseki-cli.d.ts +38 -0
- package/dist/kaseki-cli.d.ts.map +1 -0
- package/dist/kaseki-cli.js +559 -0
- package/dist/kaseki-cli.js.map +1 -0
- package/dist/kaseki-report.d.ts +3 -0
- package/dist/kaseki-report.d.ts.map +1 -0
- package/dist/kaseki-report.js +140 -0
- package/dist/kaseki-report.js.map +1 -0
- package/dist/lib/subprocess-helpers.d.ts +98 -0
- package/dist/lib/subprocess-helpers.d.ts.map +1 -0
- package/dist/lib/subprocess-helpers.js +136 -0
- package/dist/lib/subprocess-helpers.js.map +1 -0
- package/dist/logger.d.ts +39 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +79 -0
- package/dist/logger.js.map +1 -0
- package/dist/metrics.d.ts +19 -0
- package/dist/metrics.d.ts.map +1 -0
- package/dist/metrics.js +59 -0
- package/dist/metrics.js.map +1 -0
- package/dist/middleware/job-lookup.d.ts +27 -0
- package/dist/middleware/job-lookup.d.ts.map +1 -0
- package/dist/middleware/job-lookup.js +28 -0
- package/dist/middleware/job-lookup.js.map +1 -0
- package/dist/pi-event-filter.d.ts +3 -0
- package/dist/pi-event-filter.d.ts.map +1 -0
- package/dist/pi-event-filter.js +126 -0
- package/dist/pi-event-filter.js.map +1 -0
- package/dist/pi-progress-stream.d.ts +3 -0
- package/dist/pi-progress-stream.d.ts.map +1 -0
- package/dist/pi-progress-stream.js +205 -0
- package/dist/pi-progress-stream.js.map +1 -0
- package/dist/pi-progress-summarizer.d.ts +61 -0
- package/dist/pi-progress-summarizer.d.ts.map +1 -0
- package/dist/pi-progress-summarizer.js +246 -0
- package/dist/pi-progress-summarizer.js.map +1 -0
- package/dist/pre-flight-validator.d.ts +72 -0
- package/dist/pre-flight-validator.d.ts.map +1 -0
- package/dist/pre-flight-validator.js +513 -0
- package/dist/pre-flight-validator.js.map +1 -0
- package/dist/progress-stream-utils.d.ts +3 -0
- package/dist/progress-stream-utils.d.ts.map +1 -0
- package/dist/progress-stream-utils.js +15 -0
- package/dist/progress-stream-utils.js.map +1 -0
- package/dist/result-cache.d.ts +52 -0
- package/dist/result-cache.d.ts.map +1 -0
- package/dist/result-cache.js +134 -0
- package/dist/result-cache.js.map +1 -0
- package/dist/routes/artifact-routes.d.ts +10 -0
- package/dist/routes/artifact-routes.d.ts.map +1 -0
- package/dist/routes/artifact-routes.js +126 -0
- package/dist/routes/artifact-routes.js.map +1 -0
- package/dist/routes/log-routes.d.ts +8 -0
- package/dist/routes/log-routes.d.ts.map +1 -0
- package/dist/routes/log-routes.js +345 -0
- package/dist/routes/log-routes.js.map +1 -0
- package/dist/routes/status-routes.d.ts +8 -0
- package/dist/routes/status-routes.d.ts.map +1 -0
- package/dist/routes/status-routes.js +82 -0
- package/dist/routes/status-routes.js.map +1 -0
- package/dist/routes/webhook-routes.d.ts +6 -0
- package/dist/routes/webhook-routes.d.ts.map +1 -0
- package/dist/routes/webhook-routes.js +86 -0
- package/dist/routes/webhook-routes.js.map +1 -0
- package/dist/run-artifact-metadata-cache.d.ts +42 -0
- package/dist/run-artifact-metadata-cache.d.ts.map +1 -0
- package/dist/run-artifact-metadata-cache.js +139 -0
- package/dist/run-artifact-metadata-cache.js.map +1 -0
- package/dist/secret-value-cache.d.ts +13 -0
- package/dist/secret-value-cache.d.ts.map +1 -0
- package/dist/secret-value-cache.js +44 -0
- package/dist/secret-value-cache.js.map +1 -0
- package/dist/secrets/SecretsManager.d.ts +80 -0
- package/dist/secrets/SecretsManager.d.ts.map +1 -0
- package/dist/secrets/SecretsManager.js +306 -0
- package/dist/secrets/SecretsManager.js.map +1 -0
- package/dist/test-utils.d.ts +55 -0
- package/dist/test-utils.d.ts.map +1 -0
- package/dist/test-utils.js +48 -0
- package/dist/test-utils.js.map +1 -0
- package/dist/timestamp-tracker.d.ts +75 -0
- package/dist/timestamp-tracker.d.ts.map +1 -0
- package/dist/timestamp-tracker.js +121 -0
- package/dist/timestamp-tracker.js.map +1 -0
- package/dist/utils/failure-artifact-writer.d.ts +29 -0
- package/dist/utils/failure-artifact-writer.d.ts.map +1 -0
- package/dist/utils/failure-artifact-writer.js +157 -0
- package/dist/utils/failure-artifact-writer.js.map +1 -0
- package/dist/utils/file-helpers.d.ts +41 -0
- package/dist/utils/file-helpers.d.ts.map +1 -0
- package/dist/utils/file-helpers.js +143 -0
- package/dist/utils/file-helpers.js.map +1 -0
- package/dist/utils/http-client-factory.d.ts +46 -0
- package/dist/utils/http-client-factory.d.ts.map +1 -0
- package/dist/utils/http-client-factory.js +114 -0
- package/dist/utils/http-client-factory.js.map +1 -0
- package/dist/utils/progress-normalizer.d.ts +13 -0
- package/dist/utils/progress-normalizer.d.ts.map +1 -0
- package/dist/utils/progress-normalizer.js +57 -0
- package/dist/utils/progress-normalizer.js.map +1 -0
- package/dist/utils/response-helpers.d.ts +34 -0
- package/dist/utils/response-helpers.d.ts.map +1 -0
- package/dist/utils/response-helpers.js +78 -0
- package/dist/utils/response-helpers.js.map +1 -0
- package/dist/utils/route-helpers.d.ts +17 -0
- package/dist/utils/route-helpers.d.ts.map +1 -0
- package/dist/utils/route-helpers.js +22 -0
- package/dist/utils/route-helpers.js.map +1 -0
- package/dist/utils/status-response-builder.d.ts +23 -0
- package/dist/utils/status-response-builder.d.ts.map +1 -0
- package/dist/utils/status-response-builder.js +144 -0
- package/dist/utils/status-response-builder.js.map +1 -0
- package/dist/utils/type-guards.d.ts +37 -0
- package/dist/utils/type-guards.d.ts.map +1 -0
- package/dist/utils/type-guards.js +45 -0
- package/dist/utils/type-guards.js.map +1 -0
- package/dist/utils/utf8-helpers.d.ts +32 -0
- package/dist/utils/utf8-helpers.d.ts.map +1 -0
- package/dist/utils/utf8-helpers.js +97 -0
- package/dist/utils/utf8-helpers.js.map +1 -0
- package/dist/utils/webhook-event-builder.d.ts +26 -0
- package/dist/utils/webhook-event-builder.d.ts.map +1 -0
- package/dist/utils/webhook-event-builder.js +77 -0
- package/dist/utils/webhook-event-builder.js.map +1 -0
- package/dist/webhook-manager.d.ts +56 -0
- package/dist/webhook-manager.d.ts.map +1 -0
- package/dist/webhook-manager.js +359 -0
- package/dist/webhook-manager.js.map +1 -0
- package/docker/workspace-cache/package-lock.json +13 -0
- package/docker/workspace-cache/package.json +7 -0
- package/docker-compose.yml +53 -0
- package/docs/API.md +708 -0
- package/docs/BACKLOG.md +19 -0
- package/docs/BUILD_STRATEGY.md +404 -0
- package/docs/CLI.md +569 -0
- package/docs/DEPLOYMENT.md +521 -0
- package/docs/DEVELOPMENT.md +459 -0
- package/docs/DOCKER_SETUP.md +522 -0
- package/docs/ENHANCED_PROGRESS_LOGS.md +264 -0
- package/docs/IMPLEMENTATION_SUMMARY.md +549 -0
- package/docs/INTEGRATION_EXAMPLE.md +217 -0
- package/docs/NPM_SETUP.md +468 -0
- package/docs/PHASE1-4_IMPLEMENTATION.md +302 -0
- package/docs/PHASE1_COMPLETION.md +192 -0
- package/docs/PHASE2_COMPLETION.md +134 -0
- package/docs/PHASE6_MIGRATION.md +392 -0
- package/docs/PRINTF_SAFETY_FIX.md +282 -0
- package/docs/QUALITY_GATES.md +369 -0
- package/docs/SETUP_GUIDE.md +482 -0
- package/docs/TASK_PROMPT_TEMPLATES.md +533 -0
- package/docs/VALIDATION_FIX.md +139 -0
- package/docs/VERIFICATION_CHECKLIST.md +335 -0
- package/docs/repo-maturity.md +760 -0
- package/fix-tests.d.ts +9 -0
- package/fix-tests.d.ts.map +1 -0
- package/fix-tests.js.map +1 -0
- package/fix-tests.ts +53 -0
- package/jest.config.ts +31 -0
- package/kaseki +183 -0
- package/kaseki-agent.sh +1961 -0
- package/ops/logrotate/kaseki +10 -0
- package/package.json +83 -0
- package/perf/README.md +54 -0
- package/perf/pi-event-filter.benchmark.test.ts +98 -0
- package/run-kaseki-json.test.sh +106 -0
- package/run-kaseki.sh +990 -0
- package/scripts/allowlist-helper.sh +56 -0
- package/scripts/cleanup-kaseki.sh +168 -0
- package/scripts/deploy-pi-template.sh +293 -0
- package/scripts/docker-entrypoint.sh +71 -0
- package/scripts/dry-run-allowlist.sh +161 -0
- package/scripts/kaseki-activate.sh +396 -0
- package/scripts/kaseki-api.service +62 -0
- package/scripts/kaseki-container-entrypoint-wrapper.sh +119 -0
- package/scripts/kaseki-container-setup-remote.sh +172 -0
- package/scripts/kaseki-container-setup.sh +193 -0
- package/scripts/kaseki-healthcheck.sh +95 -0
- package/scripts/kaseki-install.sh +50 -0
- package/scripts/kaseki-maturity-score.sh +291 -0
- package/scripts/kaseki-performance-metrics.sh +122 -0
- package/scripts/kaseki-preflight.sh +270 -0
- package/scripts/kaseki-setup.sh +265 -0
- package/scripts/pi-setup-remote.sh +213 -0
- package/scripts/setup-github-labels.sh +42 -0
- package/scripts/suggest-allowlist.sh +68 -0
- package/scripts/templates/MULTI_HOST_DISTRIBUTED.md +337 -0
- package/scripts/templates/REST_API_SERVICE.md +490 -0
- package/scripts/templates/SINGLE_HOST_CLI.md +194 -0
- package/scripts/test-github-app.sh +248 -0
- package/src/add-js-extensions.ts +61 -0
- package/src/ansi-colors.test.ts +62 -0
- package/src/ansi-colors.ts +67 -0
- package/src/cli/BaseCommand.ts +40 -0
- package/src/cli/KasekiCLI.ts +154 -0
- package/src/cli/commands/ConfigCommand.ts +145 -0
- package/src/cli/commands/DoctorCommand.ts +329 -0
- package/src/cli/commands/ListCommand.ts +105 -0
- package/src/cli/commands/ReportCommand.ts +110 -0
- package/src/cli/commands/RunCommand.ts +218 -0
- package/src/cli/commands/SecretsCommand.ts +120 -0
- package/src/cli/commands/ServeCommand.ts +62 -0
- package/src/cli/commands/SetupCommand.ts +301 -0
- package/src/cli.ts +138 -0
- package/src/config/ConfigManager.ts +476 -0
- package/src/docker/DockerManager.ts +319 -0
- package/src/docker-entrypoint-packaging.test.ts +33 -0
- package/src/event-aggregator.test.ts +117 -0
- package/src/event-aggregator.ts +126 -0
- package/src/github-app-token.ts +215 -0
- package/src/idempotency-store.test.ts +117 -0
- package/src/idempotency-store.ts +385 -0
- package/src/index.ts +89 -0
- package/src/instance/InstanceManager.ts +285 -0
- package/src/instance-metadata-reader.test.ts +190 -0
- package/src/instance-metadata-reader.ts +129 -0
- package/src/instance-state-derivation.test.ts +263 -0
- package/src/instance-state-derivation.ts +148 -0
- package/src/job-scheduler.test.ts +1236 -0
- package/src/job-scheduler.ts +1117 -0
- package/src/kaseki-api-client.ts +488 -0
- package/src/kaseki-api-config.test.ts +315 -0
- package/src/kaseki-api-config.ts +175 -0
- package/src/kaseki-api-routes.test.ts +1615 -0
- package/src/kaseki-api-routes.ts +643 -0
- package/src/kaseki-api-service-wrapper.ts +188 -0
- package/src/kaseki-api-service.test.ts +418 -0
- package/src/kaseki-api-service.ts +192 -0
- package/src/kaseki-api-types.ts +320 -0
- package/src/kaseki-cli-lib.test.ts +552 -0
- package/src/kaseki-cli-lib.ts +760 -0
- package/src/kaseki-cli.ts +682 -0
- package/src/kaseki-report.test.ts +118 -0
- package/src/kaseki-report.ts +192 -0
- package/src/lib/subprocess-helpers.ts +177 -0
- package/src/logger.ts +114 -0
- package/src/metrics.ts +66 -0
- package/src/middleware/job-lookup.test.ts +113 -0
- package/src/middleware/job-lookup.ts +45 -0
- package/src/pi-event-filter.test.ts +183 -0
- package/src/pi-event-filter.ts +183 -0
- package/src/pi-progress-stream.ts +287 -0
- package/src/pi-progress-summarizer.test.ts +302 -0
- package/src/pi-progress-summarizer.ts +287 -0
- package/src/pre-flight-validator.test.ts +512 -0
- package/src/pre-flight-validator.ts +618 -0
- package/src/progress-stream-utils.test.ts +35 -0
- package/src/progress-stream-utils.ts +14 -0
- package/src/result-cache.test.ts +195 -0
- package/src/result-cache.ts +181 -0
- package/src/routes/artifact-routes.ts +169 -0
- package/src/routes/log-routes.ts +391 -0
- package/src/routes/status-routes.ts +92 -0
- package/src/routes/webhook-routes.ts +97 -0
- package/src/run-artifact-metadata-cache.test.ts +80 -0
- package/src/run-artifact-metadata-cache.ts +184 -0
- package/src/secret-value-cache.test.ts +66 -0
- package/src/secret-value-cache.ts +55 -0
- package/src/secrets/SecretsManager.ts +343 -0
- package/src/test-utils.ts +81 -0
- package/src/timestamp-tracker.test.ts +134 -0
- package/src/timestamp-tracker.ts +132 -0
- package/src/utils/failure-artifact-writer.ts +187 -0
- package/src/utils/file-helpers.test.ts +235 -0
- package/src/utils/file-helpers.ts +150 -0
- package/src/utils/http-client-factory.test.ts +245 -0
- package/src/utils/http-client-factory.ts +157 -0
- package/src/utils/progress-normalizer.test.ts +442 -0
- package/src/utils/progress-normalizer.ts +68 -0
- package/src/utils/response-helpers.test.ts +122 -0
- package/src/utils/response-helpers.ts +101 -0
- package/src/utils/route-helpers.ts +30 -0
- package/src/utils/status-response-builder.ts +159 -0
- package/src/utils/type-guards.ts +52 -0
- package/src/utils/utf8-helpers.ts +102 -0
- package/src/utils/webhook-event-builder.test.ts +143 -0
- package/src/utils/webhook-event-builder.ts +87 -0
- package/src/webhook-manager.test.ts +152 -0
- package/src/webhook-manager.ts +445 -0
- package/templates/allowlist-api-route.txt +7 -0
- package/templates/allowlist-comprehensive.txt +8 -0
- package/templates/allowlist-parser-fix.txt +6 -0
- package/templates/allowlist-ui-component.txt +9 -0
- package/templates/allowlist-utility.txt +9 -0
- package/test/actual-model-metadata.test.sh +102 -0
- package/test/dry-run.test.sh +131 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-0.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-1.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-invalid.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-0.json +1 -0
- package/test/fixtures/kaseki-report-exit-codes/metadata-exit-str-1.json +1 -0
- package/test/kaseki-api.integration.test.sh +165 -0
- package/test/pi-event-filter-failure.test.sh +83 -0
- package/test/printf-safety-focused.test.sh +99 -0
- package/test/printf-safety-results/results/restoration.jsonl +10 -0
- package/test/printf-safety-results/results/test.jsonl +0 -0
- package/test/printf-safety.test.sh +297 -0
- package/test/validation-fix.test.sh +79 -0
- package/test/validation-integration.test.sh +109 -0
- package/tests/allowlist-glob.test.sh +61 -0
- package/tests/dependency-cache-key.test.sh +48 -0
- package/tests/dependency-restore-mode.test.sh +48 -0
- package/tests/doctor-template-parity.test.sh +95 -0
- package/tests/github-operations.test.sh +142 -0
- package/tests/npm-install-flags.test.sh +58 -0
- package/tests/quality-gates.test.sh +178 -0
- package/tests/repo-memory.test.sh +103 -0
- package/tests/restore-disallowed-changes.test.sh +80 -0
- package/tests/validation-missing-npm-scripts.test.sh +93 -0
- package/tests/validation-strict-mode.test.sh +118 -0
- package/tsconfig.changed.json +7 -0
- package/tsconfig.json +39 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run Command
|
|
3
|
+
* Execute kaseki agent on target repository
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseCommand } from '../BaseCommand';
|
|
7
|
+
import { DockerManager } from '../../docker/DockerManager';
|
|
8
|
+
import { InstanceManager } from '../../instance/InstanceManager';
|
|
9
|
+
import { DoctorCommand } from './DoctorCommand';
|
|
10
|
+
import { createLogger } from '../../logger';
|
|
11
|
+
|
|
12
|
+
const logger = createLogger('run-cmd');
|
|
13
|
+
|
|
14
|
+
export class RunCommand extends BaseCommand {
|
|
15
|
+
async execute(args: string[]): Promise<number> {
|
|
16
|
+
try {
|
|
17
|
+
const { positional } = this.parseArgs(args);
|
|
18
|
+
|
|
19
|
+
// Parse arguments
|
|
20
|
+
const repoUrl = positional[0] || this.configManager.get('repo.url');
|
|
21
|
+
const gitRef = positional[1] || this.configManager.get('repo.ref');
|
|
22
|
+
const taskPrompt = positional[2] || this.configManager.get('repo.task_prompt', '');
|
|
23
|
+
|
|
24
|
+
if (!repoUrl || !gitRef) {
|
|
25
|
+
console.error('Usage: kaseki-agent run <REPO_URL> [GIT_REF] [TASK_PROMPT]');
|
|
26
|
+
console.error('Example: kaseki-agent run https://github.com/org/repo main');
|
|
27
|
+
return 1;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
console.log('🚀 Kaseki Agent Runner\n');
|
|
31
|
+
console.log(`Repository: ${repoUrl}`);
|
|
32
|
+
console.log(`Branch: ${gitRef}\n`);
|
|
33
|
+
|
|
34
|
+
// Load configuration
|
|
35
|
+
await this.configManager.load();
|
|
36
|
+
|
|
37
|
+
// Step 1: Pre-flight checks
|
|
38
|
+
console.log('Step 1/6: Running pre-flight checks...');
|
|
39
|
+
const doctorCmd = new DoctorCommand(this.configManager);
|
|
40
|
+
const doctorResult = await doctorCmd.execute(['--json']);
|
|
41
|
+
if (doctorResult !== 0) {
|
|
42
|
+
console.error('❌ Pre-flight checks failed');
|
|
43
|
+
return 1;
|
|
44
|
+
}
|
|
45
|
+
console.log('✓ Pre-flight checks passed\n');
|
|
46
|
+
|
|
47
|
+
// Step 2: Pull Docker image
|
|
48
|
+
console.log('Step 2/6: Preparing Docker image...');
|
|
49
|
+
const image = this.configManager.get('docker.image');
|
|
50
|
+
const autoPull = this.configManager.get('docker.auto_pull', true);
|
|
51
|
+
|
|
52
|
+
if (!DockerManager.imageExists(image)) {
|
|
53
|
+
if (!autoPull) {
|
|
54
|
+
console.error(`❌ Docker image not found: ${image}`);
|
|
55
|
+
console.error('Enable auto-pull or pull manually: docker pull ' + image);
|
|
56
|
+
return 1;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log('Image not found locally, pulling from registry...');
|
|
60
|
+
if (!DockerManager.pullImage(image)) {
|
|
61
|
+
console.error(`❌ Failed to pull Docker image: ${image}`);
|
|
62
|
+
return 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
console.log(`✓ Docker image ready: ${image}\n`);
|
|
66
|
+
|
|
67
|
+
// Step 3: Create instance
|
|
68
|
+
console.log('Step 3/6: Creating instance...');
|
|
69
|
+
const kasekiRoot = this.configManager.get('directories.root');
|
|
70
|
+
const instanceManager = new InstanceManager(kasekiRoot);
|
|
71
|
+
const instanceId = await instanceManager.getOrCreateInstanceId();
|
|
72
|
+
|
|
73
|
+
const { workspace, results } = await instanceManager.createDirectories();
|
|
74
|
+
console.log(`✓ Instance created: ${instanceId}`);
|
|
75
|
+
console.log(` Workspace: ${workspace}`);
|
|
76
|
+
console.log(` Results: ${results}\n`);
|
|
77
|
+
|
|
78
|
+
// Initialize metadata
|
|
79
|
+
await instanceManager.initializeMetadata({
|
|
80
|
+
repoUrl,
|
|
81
|
+
gitRef,
|
|
82
|
+
model: this.configManager.get('agent.model'),
|
|
83
|
+
provider: this.configManager.get('agent.provider'),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Step 4: Prepare environment
|
|
87
|
+
console.log('Step 4/6: Preparing environment...');
|
|
88
|
+
const environment = this.buildEnvironment(repoUrl, gitRef, taskPrompt);
|
|
89
|
+
const apiKeyFile = this.configManager.get('auth.openrouter_api_key_file');
|
|
90
|
+
console.log('✓ Environment prepared\n');
|
|
91
|
+
|
|
92
|
+
// Step 5: Run agent in Docker
|
|
93
|
+
console.log('Step 5/6: Running kaseki agent in Docker...\n');
|
|
94
|
+
const timeout = this.configManager.get('agent.timeout_seconds', 1200);
|
|
95
|
+
|
|
96
|
+
const stageStart = new Date();
|
|
97
|
+
const containerResult = await DockerManager.runContainer({
|
|
98
|
+
image,
|
|
99
|
+
name: instanceId,
|
|
100
|
+
workspaceDir: workspace,
|
|
101
|
+
resultsDir: results,
|
|
102
|
+
cacheDir: this.configManager.get('directories.cache_dir'),
|
|
103
|
+
apiKeyFile,
|
|
104
|
+
environment,
|
|
105
|
+
timeout,
|
|
106
|
+
entrypoint: '/agents/kaseki-agent.sh',
|
|
107
|
+
command: [],
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const stageEnd = new Date();
|
|
111
|
+
await instanceManager.recordStage('agent-run', containerResult.exitCode, stageStart, stageEnd);
|
|
112
|
+
|
|
113
|
+
console.log('\n✓ Agent execution completed\n');
|
|
114
|
+
|
|
115
|
+
// Step 6: Finalize
|
|
116
|
+
console.log('Step 6/6: Finalizing...');
|
|
117
|
+
await instanceManager.finalize(containerResult.exitCode);
|
|
118
|
+
|
|
119
|
+
// Report summary
|
|
120
|
+
const metadata = await instanceManager.getMetadata();
|
|
121
|
+
if (metadata) {
|
|
122
|
+
console.log('\n📊 Run Summary');
|
|
123
|
+
console.log(`Instance: ${instanceId}`);
|
|
124
|
+
console.log(`Status: ${metadata.status}`);
|
|
125
|
+
console.log(`Duration: ${metadata.stages?.['agent-run']?.duration?.toFixed(1)}s`);
|
|
126
|
+
|
|
127
|
+
if (containerResult.exitCode === 0) {
|
|
128
|
+
console.log('\n✅ Run completed successfully');
|
|
129
|
+
console.log(`View results: kaseki-agent report ${instanceId}`);
|
|
130
|
+
} else {
|
|
131
|
+
console.log(`\n❌ Run failed with exit code ${containerResult.exitCode}`);
|
|
132
|
+
console.log(`View logs: kaseki-agent report ${instanceId}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return containerResult.exitCode;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
logger.error(`Run failed: ${error}`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Build environment variables for container
|
|
145
|
+
*/
|
|
146
|
+
private buildEnvironment(repoUrl: string, gitRef: string, taskPrompt: string): Record<string, string> {
|
|
147
|
+
const env: Record<string, string> = {};
|
|
148
|
+
|
|
149
|
+
// Repo configuration
|
|
150
|
+
env.REPO_URL = repoUrl;
|
|
151
|
+
env.GIT_REF = gitRef;
|
|
152
|
+
|
|
153
|
+
// Agent configuration
|
|
154
|
+
const model = this.configManager.get('agent.model');
|
|
155
|
+
if (model) env.KASEKI_MODEL = model;
|
|
156
|
+
|
|
157
|
+
const provider = this.configManager.get('agent.provider');
|
|
158
|
+
if (provider) env.KASEKI_PROVIDER = provider;
|
|
159
|
+
|
|
160
|
+
const timeout = this.configManager.get('agent.timeout_seconds');
|
|
161
|
+
if (timeout) env.KASEKI_AGENT_TIMEOUT_SECONDS = String(timeout);
|
|
162
|
+
|
|
163
|
+
// Task prompt
|
|
164
|
+
if (taskPrompt) {
|
|
165
|
+
env.TASK_PROMPT = taskPrompt;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Validation
|
|
169
|
+
const validationCommands = this.configManager.get('validation.commands', []);
|
|
170
|
+
if (validationCommands.length > 0) {
|
|
171
|
+
env.KASEKI_VALIDATION_COMMANDS = validationCommands.join(';');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const skipMissingScripts = this.configManager.get('validation.skip_missing_npm_scripts');
|
|
175
|
+
if (skipMissingScripts) env.KASEKI_SKIP_MISSING_NPM_SCRIPTS = '1';
|
|
176
|
+
|
|
177
|
+
const failFast = this.configManager.get('validation.fail_fast');
|
|
178
|
+
if (failFast !== undefined) env.KASEKI_VALIDATION_FAIL_FAST = failFast ? '1' : '0';
|
|
179
|
+
|
|
180
|
+
// Allowlist
|
|
181
|
+
const allowlist = this.configManager.get('validation.allowlist', []);
|
|
182
|
+
if (allowlist.length > 0) {
|
|
183
|
+
env.KASEKI_CHANGED_FILES_ALLOWLIST = allowlist.join(' ');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const maxDiffBytes = this.configManager.get('validation.max_diff_bytes');
|
|
187
|
+
if (maxDiffBytes) env.KASEKI_MAX_DIFF_BYTES = String(maxDiffBytes);
|
|
188
|
+
|
|
189
|
+
// Caching
|
|
190
|
+
const cacheMode = this.configManager.get('caching.dependency_restore_mode');
|
|
191
|
+
if (cacheMode) env.KASEKI_DEPENDENCY_RESTORE_MODE = cacheMode;
|
|
192
|
+
|
|
193
|
+
// GitHub integration
|
|
194
|
+
const ghAppId = this.configManager.get('auth.github_app_id_file');
|
|
195
|
+
if (ghAppId) env.GITHUB_APP_ID_FILE = ghAppId;
|
|
196
|
+
|
|
197
|
+
const ghClientId = this.configManager.get('auth.github_app_client_id_file');
|
|
198
|
+
if (ghClientId) env.GITHUB_APP_CLIENT_ID_FILE = ghClientId;
|
|
199
|
+
|
|
200
|
+
const ghPrivateKey = this.configManager.get('auth.github_app_private_key_file');
|
|
201
|
+
if (ghPrivateKey) env.GITHUB_APP_PRIVATE_KEY_FILE = ghPrivateKey;
|
|
202
|
+
|
|
203
|
+
// API key file (never inline)
|
|
204
|
+
const apiKeyFile = this.configManager.get('auth.openrouter_api_key_file');
|
|
205
|
+
if (apiKeyFile) {
|
|
206
|
+
env.OPENROUTER_API_KEY_FILE = '/run/secrets/openrouter_api_key';
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Debug flags
|
|
210
|
+
const streamProgress = this.configManager.get('debug.stream_progress');
|
|
211
|
+
if (streamProgress !== undefined) env.KASEKI_STREAM_PROGRESS = streamProgress ? '1' : '0';
|
|
212
|
+
|
|
213
|
+
const keepWorkspace = this.configManager.get('debug.keep_workspace');
|
|
214
|
+
if (keepWorkspace !== undefined) env.KASEKI_KEEP_WORKSPACE = keepWorkspace ? '1' : '0';
|
|
215
|
+
|
|
216
|
+
return env;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secrets Command
|
|
3
|
+
* Manage secrets (API keys, credentials)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseCommand } from '../BaseCommand';
|
|
7
|
+
import { SecretsManager } from '../../secrets/SecretsManager';
|
|
8
|
+
import { createLogger } from '../../logger';
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('secrets-cmd');
|
|
11
|
+
|
|
12
|
+
export class SecretsCommand extends BaseCommand {
|
|
13
|
+
async execute(args: string[]): Promise<number> {
|
|
14
|
+
try {
|
|
15
|
+
const { positional, flags } = this.parseArgs(args);
|
|
16
|
+
const subcommand = positional[0];
|
|
17
|
+
const secretKey = positional[1];
|
|
18
|
+
const secretValue = positional[2];
|
|
19
|
+
|
|
20
|
+
const secretsManager = new SecretsManager();
|
|
21
|
+
|
|
22
|
+
switch (subcommand) {
|
|
23
|
+
case 'init': {
|
|
24
|
+
console.log('🔐 Initializing secrets store...\n');
|
|
25
|
+
await secretsManager.initializeKeyring();
|
|
26
|
+
console.log('✓ Secrets store initialized');
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
case 'set': {
|
|
31
|
+
if (!secretKey || !secretValue) {
|
|
32
|
+
console.error('Usage: kaseki-agent secrets set <KEY> <VALUE>');
|
|
33
|
+
console.error('Example: kaseki-agent secrets set openrouter-api-key sk-or-...');
|
|
34
|
+
return 1;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await secretsManager.store(secretKey, secretValue);
|
|
38
|
+
console.log(`✓ Stored secret: ${secretKey}`);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
case 'get': {
|
|
43
|
+
if (!secretKey) {
|
|
44
|
+
console.error('Usage: kaseki-agent secrets get <KEY>');
|
|
45
|
+
return 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const value = await secretsManager.retrieve(secretKey);
|
|
49
|
+
if (value) {
|
|
50
|
+
// Only show if explicitly requested with --show
|
|
51
|
+
if (flags.has('show')) {
|
|
52
|
+
console.log(value);
|
|
53
|
+
} else {
|
|
54
|
+
console.log(`✓ Secret exists: ${secretKey}`);
|
|
55
|
+
console.log('(Use --show to display the value)');
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
console.log(`Secret not found: ${secretKey}`);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
return 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 'delete': {
|
|
65
|
+
if (!secretKey) {
|
|
66
|
+
console.error('Usage: kaseki-agent secrets delete <KEY>');
|
|
67
|
+
return 1;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
await secretsManager.delete(secretKey);
|
|
71
|
+
console.log(`✓ Deleted secret: ${secretKey}`);
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
case 'list': {
|
|
76
|
+
const secrets = await secretsManager.list();
|
|
77
|
+
if (secrets.size === 0) {
|
|
78
|
+
console.log('No secrets stored');
|
|
79
|
+
return 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log('📋 Stored Secrets\n');
|
|
83
|
+
for (const key of secrets.keys()) {
|
|
84
|
+
console.log(` • ${key}`);
|
|
85
|
+
}
|
|
86
|
+
console.log(`\nTotal: ${secrets.size} secret(s)`);
|
|
87
|
+
return 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
case 'help': {
|
|
91
|
+
console.log('🔐 Secrets Management\n');
|
|
92
|
+
console.log('Usage:');
|
|
93
|
+
console.log(' kaseki-agent secrets init Initialize keyring');
|
|
94
|
+
console.log(' kaseki-agent secrets set <KEY> <VALUE> Store a secret');
|
|
95
|
+
console.log(' kaseki-agent secrets get <KEY> [--show] Retrieve a secret');
|
|
96
|
+
console.log(' kaseki-agent secrets delete <KEY> Delete a secret');
|
|
97
|
+
console.log(' kaseki-agent secrets list List all secret keys');
|
|
98
|
+
console.log('\nCommon Keys:');
|
|
99
|
+
console.log(' openrouter-api-key OpenRouter API key');
|
|
100
|
+
console.log(' github-app-id GitHub App ID');
|
|
101
|
+
console.log(' github-app-client-id GitHub App Client ID');
|
|
102
|
+
console.log(' github-app-private-key GitHub App Private Key\n');
|
|
103
|
+
console.log('Storage:');
|
|
104
|
+
console.log(' - Uses Linux pass (password-store) by default');
|
|
105
|
+
console.log(' - Falls back to ~/.kaseki/secrets/ with 0600 permissions');
|
|
106
|
+
console.log(' - Keys are never exposed via environment variables');
|
|
107
|
+
return 0;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
default:
|
|
111
|
+
console.error('Unknown subcommand: ' + subcommand);
|
|
112
|
+
console.error('\nRun: kaseki-agent secrets help');
|
|
113
|
+
return 1;
|
|
114
|
+
}
|
|
115
|
+
} catch (error) {
|
|
116
|
+
logger.error(`Secrets command failed: ${error}`);
|
|
117
|
+
return 1;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serve Command
|
|
3
|
+
* Start kaseki API service
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { BaseCommand } from '../BaseCommand';
|
|
7
|
+
import { KasekiAPIService } from '../../kaseki-api-service-wrapper';
|
|
8
|
+
import { createLogger } from '../../logger';
|
|
9
|
+
|
|
10
|
+
const logger = createLogger('serve-cmd');
|
|
11
|
+
|
|
12
|
+
export class ServeCommand extends BaseCommand {
|
|
13
|
+
async execute(args: string[]): Promise<number> {
|
|
14
|
+
try {
|
|
15
|
+
const { flags } = this.parseArgs(args);
|
|
16
|
+
const portValue = flags.get('port');
|
|
17
|
+
const port = parseInt(
|
|
18
|
+
(typeof portValue === 'string' ? portValue : undefined) || '8080',
|
|
19
|
+
10
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
console.log('🚀 Kaseki API Service\n');
|
|
23
|
+
|
|
24
|
+
await this.configManager.load();
|
|
25
|
+
|
|
26
|
+
// Get API keys from config or environment
|
|
27
|
+
const apiKeysEnv = process.env.KASEKI_API_KEYS || '';
|
|
28
|
+
const apiKeys = apiKeysEnv ? apiKeysEnv.split(',') : [];
|
|
29
|
+
|
|
30
|
+
// Create and start service
|
|
31
|
+
const apiService = new KasekiAPIService({
|
|
32
|
+
port,
|
|
33
|
+
apiKeys,
|
|
34
|
+
logLevel: this.configManager.get('debug.log_level', 'info'),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
await apiService.start();
|
|
38
|
+
|
|
39
|
+
console.log('Press Ctrl+C to stop the service\n');
|
|
40
|
+
|
|
41
|
+
// Keep the process alive
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
process.on('SIGINT', async () => {
|
|
44
|
+
console.log('\n\nShutting down...');
|
|
45
|
+
await apiService.stop();
|
|
46
|
+
console.log('✓ Service stopped');
|
|
47
|
+
resolve(0);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
process.on('SIGTERM', async () => {
|
|
51
|
+
console.log('\n\nShutting down...');
|
|
52
|
+
await apiService.stop();
|
|
53
|
+
console.log('✓ Service stopped');
|
|
54
|
+
resolve(0);
|
|
55
|
+
});
|
|
56
|
+
});
|
|
57
|
+
} catch (error) {
|
|
58
|
+
logger.error(`Serve failed: ${error}`);
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup Command
|
|
3
|
+
* Interactive setup wizard for first-time configuration
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import readline from 'readline';
|
|
7
|
+
import { execSync } from 'child_process';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
import { SecretsManager } from '../../secrets/SecretsManager';
|
|
11
|
+
import { DoctorCommand } from './DoctorCommand';
|
|
12
|
+
import { BaseCommand } from '../BaseCommand';
|
|
13
|
+
import { createLogger } from '../../logger';
|
|
14
|
+
|
|
15
|
+
const logger = createLogger('setup-cmd');
|
|
16
|
+
|
|
17
|
+
interface SetupAnswers {
|
|
18
|
+
apiKey: string;
|
|
19
|
+
configLocation: 'project' | 'global';
|
|
20
|
+
validationCommands: string[];
|
|
21
|
+
modelName: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class SetupCommand extends BaseCommand {
|
|
25
|
+
private rl: readline.Interface | null = null;
|
|
26
|
+
|
|
27
|
+
async execute(_args: string[]): Promise<number> {
|
|
28
|
+
try {
|
|
29
|
+
console.log('\n🔧 Kaseki Agent Setup Wizard\n');
|
|
30
|
+
console.log('This will configure kaseki-agent for your system.\n');
|
|
31
|
+
|
|
32
|
+
// Step 1: Check Docker
|
|
33
|
+
console.log('Step 1/5: Checking Docker installation...');
|
|
34
|
+
if (!this.isDockerInstalled()) {
|
|
35
|
+
console.error(
|
|
36
|
+
'❌ Docker is not installed or not accessible.\n' +
|
|
37
|
+
'Please install Docker and ensure the daemon is running:\n' +
|
|
38
|
+
' https://docs.docker.com/install/\n\n' +
|
|
39
|
+
'After installation, run: kaseki-agent setup\n'
|
|
40
|
+
);
|
|
41
|
+
return 1;
|
|
42
|
+
}
|
|
43
|
+
console.log('✓ Docker found and accessible\n');
|
|
44
|
+
|
|
45
|
+
// Step 2: Check Node.js
|
|
46
|
+
console.log('Step 2/5: Checking Node.js...');
|
|
47
|
+
const nodeVersion = this.getNodeVersion();
|
|
48
|
+
if (!nodeVersion || !this.isNodeVersionValid(nodeVersion)) {
|
|
49
|
+
console.error(`❌ Node.js v24+ is required. Found: ${nodeVersion || 'not installed'}`);
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
console.log(`✓ Node.js ${nodeVersion} detected\n`);
|
|
53
|
+
|
|
54
|
+
// Step 3: Collect configuration
|
|
55
|
+
console.log('Step 3/5: Configuring OpenRouter API key...');
|
|
56
|
+
const answers = await this.promptForAnswers();
|
|
57
|
+
if (!answers) {
|
|
58
|
+
console.log('Setup cancelled.');
|
|
59
|
+
return 1;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Step 4: Store configuration
|
|
63
|
+
console.log('\nStep 4/5: Saving configuration...');
|
|
64
|
+
await this.saveConfiguration(answers);
|
|
65
|
+
console.log('✓ Configuration saved\n');
|
|
66
|
+
|
|
67
|
+
// Step 5: Run health check
|
|
68
|
+
console.log('Step 5/5: Running health check...\n');
|
|
69
|
+
const doctorCmd = new DoctorCommand(this.configManager);
|
|
70
|
+
const doctorResult = await doctorCmd.execute(['--verbose']);
|
|
71
|
+
if (doctorResult !== 0) {
|
|
72
|
+
console.log(
|
|
73
|
+
'\n⚠️ Health check found issues. Review the output above.\n' +
|
|
74
|
+
'You can run "kaseki-agent doctor --fix" to auto-remediate.\n'
|
|
75
|
+
);
|
|
76
|
+
// Don't fail - configuration is still valid even if some checks failed
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
console.log(
|
|
80
|
+
'\n✅ Setup complete!\n\n' +
|
|
81
|
+
'You can now use kaseki-agent:\n' +
|
|
82
|
+
' kaseki-agent run <repo-url> <git-ref> # Run agent\n' +
|
|
83
|
+
' kaseki-agent doctor --verbose # Health check\n' +
|
|
84
|
+
' kaseki-agent serve --port 8080 # Start API\n\n' +
|
|
85
|
+
'For help: kaseki-agent --help\n'
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
return 0;
|
|
89
|
+
} catch (error) {
|
|
90
|
+
logger.error(`Setup failed: ${error}`);
|
|
91
|
+
return 1;
|
|
92
|
+
} finally {
|
|
93
|
+
this.closeReadline();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Prompt user for configuration answers
|
|
99
|
+
*/
|
|
100
|
+
private async promptForAnswers(): Promise<SetupAnswers | null> {
|
|
101
|
+
try {
|
|
102
|
+
this.rl = readline.createInterface({
|
|
103
|
+
input: process.stdin,
|
|
104
|
+
output: process.stdout,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Prompt for API key
|
|
108
|
+
const apiKey = await this.prompt(
|
|
109
|
+
'📌 Enter your OpenRouter API key (sk-or-...): ',
|
|
110
|
+
true
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (!apiKey) {
|
|
114
|
+
console.error('API key is required.');
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Verify API key format
|
|
119
|
+
if (!apiKey.startsWith('sk-or-')) {
|
|
120
|
+
console.warn('⚠️ API key should start with "sk-or-". Continuing anyway...');
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Prompt for config location
|
|
124
|
+
const configLocation = await this.promptChoice(
|
|
125
|
+
'Where should config be stored?',
|
|
126
|
+
[
|
|
127
|
+
{ label: 'Project-local (./kaseki-agent.json)', value: 'project' },
|
|
128
|
+
{ label: 'User home (~/.kaseki/config.json)', value: 'global' },
|
|
129
|
+
]
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
if (!configLocation) {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Prompt for model
|
|
137
|
+
const modelName = await this.prompt(
|
|
138
|
+
'Model identifier (default: openrouter/free): ',
|
|
139
|
+
false
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
return {
|
|
143
|
+
apiKey,
|
|
144
|
+
configLocation: configLocation as 'project' | 'global',
|
|
145
|
+
validationCommands: [
|
|
146
|
+
'npm run check',
|
|
147
|
+
'npm run test',
|
|
148
|
+
'npm run build',
|
|
149
|
+
],
|
|
150
|
+
modelName: modelName || 'openrouter/free',
|
|
151
|
+
};
|
|
152
|
+
} finally {
|
|
153
|
+
this.closeReadline();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Prompt for single answer with optional masking
|
|
159
|
+
*/
|
|
160
|
+
private prompt(question: string, mask: boolean = false): Promise<string | null> {
|
|
161
|
+
return new Promise((resolve) => {
|
|
162
|
+
if (!this.rl) {
|
|
163
|
+
resolve(null);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (mask) {
|
|
168
|
+
// Mask password input
|
|
169
|
+
process.stdout.write(question);
|
|
170
|
+
const stdin = process.stdin;
|
|
171
|
+
stdin.resume();
|
|
172
|
+
stdin.setRawMode(true);
|
|
173
|
+
|
|
174
|
+
let password = '';
|
|
175
|
+
stdin.on('data', (char: Buffer) => {
|
|
176
|
+
const charStr = char.toString('utf-8');
|
|
177
|
+
if (charStr === '\n' || charStr === '\r' || charStr === '\u0004') {
|
|
178
|
+
// Enter key or Ctrl-D
|
|
179
|
+
stdin.setRawMode(false);
|
|
180
|
+
stdin.pause();
|
|
181
|
+
console.log(); // Newline for cleanliness
|
|
182
|
+
resolve(password);
|
|
183
|
+
} else if (charStr === '\u0003') {
|
|
184
|
+
// Ctrl-C
|
|
185
|
+
process.exit();
|
|
186
|
+
} else {
|
|
187
|
+
password += charStr;
|
|
188
|
+
process.stdout.write('*');
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
this.rl!.question(question, (answer) => {
|
|
193
|
+
resolve(answer || null);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Prompt for choice from list
|
|
201
|
+
*/
|
|
202
|
+
private async promptChoice(
|
|
203
|
+
question: string,
|
|
204
|
+
choices: Array<{ label: string; value: string }>
|
|
205
|
+
): Promise<string | null> {
|
|
206
|
+
return new Promise((resolve) => {
|
|
207
|
+
if (!this.rl) {
|
|
208
|
+
resolve(null);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
console.log(`\n${question}`);
|
|
213
|
+
choices.forEach((choice, index) => {
|
|
214
|
+
console.log(` ${index + 1}) ${choice.label}`);
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
this.rl!.question('Enter number (1-' + choices.length + '): ', (answer) => {
|
|
218
|
+
const index = parseInt(answer, 10) - 1;
|
|
219
|
+
if (index >= 0 && index < choices.length) {
|
|
220
|
+
resolve(choices[index].value);
|
|
221
|
+
} else {
|
|
222
|
+
resolve(null);
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Save configuration to file and secrets
|
|
230
|
+
*/
|
|
231
|
+
private async saveConfiguration(answers: SetupAnswers): Promise<void> {
|
|
232
|
+
// Store API key securely
|
|
233
|
+
const secretsManager = new SecretsManager();
|
|
234
|
+
await secretsManager.store('openrouter_api_key', answers.apiKey);
|
|
235
|
+
|
|
236
|
+
// Determine config file path
|
|
237
|
+
const configPath = answers.configLocation === 'project'
|
|
238
|
+
? path.join(process.cwd(), 'kaseki-agent.json')
|
|
239
|
+
: path.join(os.homedir(), '.kaseki', 'config.json');
|
|
240
|
+
|
|
241
|
+
// Load current configuration
|
|
242
|
+
await this.configManager.load();
|
|
243
|
+
|
|
244
|
+
// Update configuration
|
|
245
|
+
this.configManager.set('agent.model', answers.modelName);
|
|
246
|
+
this.configManager.set('auth.openrouter_api_key_file',
|
|
247
|
+
path.join(os.homedir(), '.kaseki', 'secrets', 'openrouter_api_key')
|
|
248
|
+
);
|
|
249
|
+
this.configManager.set('validation.commands', answers.validationCommands);
|
|
250
|
+
|
|
251
|
+
// Save configuration
|
|
252
|
+
await this.configManager.save(configPath);
|
|
253
|
+
|
|
254
|
+
logger.debug(`Configuration saved to: ${configPath}`);
|
|
255
|
+
console.log(`Config saved: ${configPath}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Check if Docker is installed and running
|
|
260
|
+
*/
|
|
261
|
+
private isDockerInstalled(): boolean {
|
|
262
|
+
try {
|
|
263
|
+
execSync('docker --version', { stdio: 'ignore' });
|
|
264
|
+
// Also check if daemon is accessible
|
|
265
|
+
execSync('docker ps', { stdio: 'ignore' });
|
|
266
|
+
return true;
|
|
267
|
+
} catch {
|
|
268
|
+
return false;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Get installed Node.js version
|
|
274
|
+
*/
|
|
275
|
+
private getNodeVersion(): string | null {
|
|
276
|
+
try {
|
|
277
|
+
const version = execSync('node --version', { encoding: 'utf-8' }).trim();
|
|
278
|
+
return version.replace('v', '');
|
|
279
|
+
} catch {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Check if Node.js version is v24+
|
|
286
|
+
*/
|
|
287
|
+
private isNodeVersionValid(version: string): boolean {
|
|
288
|
+
const major = parseInt(version.split('.')[0], 10);
|
|
289
|
+
return major >= 24;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Close readline interface
|
|
294
|
+
*/
|
|
295
|
+
private closeReadline(): void {
|
|
296
|
+
if (this.rl) {
|
|
297
|
+
this.rl.close();
|
|
298
|
+
this.rl = null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|