@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,263 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import {
|
|
5
|
+
deriveInstanceLifecycleStatus,
|
|
6
|
+
resolveInstanceExitCode,
|
|
7
|
+
resolveInstanceStage,
|
|
8
|
+
classifyFailure,
|
|
9
|
+
extractValidationFailureReason,
|
|
10
|
+
extractQualityFailureReason,
|
|
11
|
+
} from './instance-state-derivation';
|
|
12
|
+
|
|
13
|
+
describe('instance-state-derivation', () => {
|
|
14
|
+
let tempDir: string;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'kaseki-test-'));
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe('deriveInstanceLifecycleStatus', () => {
|
|
25
|
+
it('should return running when isRunning is true', () => {
|
|
26
|
+
const status = deriveInstanceLifecycleStatus(true, 0);
|
|
27
|
+
expect(status).toBe('running');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('should return completed when isRunning is false and exitCode is 0', () => {
|
|
31
|
+
const status = deriveInstanceLifecycleStatus(false, 0);
|
|
32
|
+
expect(status).toBe('completed');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should return failed when isRunning is false and exitCode is non-zero', () => {
|
|
36
|
+
const status = deriveInstanceLifecycleStatus(false, 1);
|
|
37
|
+
expect(status).toBe('failed');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('should return pending when isRunning is false and exitCode is null', () => {
|
|
41
|
+
const status = deriveInstanceLifecycleStatus(false, null);
|
|
42
|
+
expect(status).toBe('pending');
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe('resolveInstanceExitCode', () => {
|
|
47
|
+
it('should read exit code from metadata', () => {
|
|
48
|
+
const metadata = { exit_code: 42 };
|
|
49
|
+
const exitCode = resolveInstanceExitCode(tempDir, metadata);
|
|
50
|
+
expect(exitCode).toBe(42);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should parse string exit code from metadata', () => {
|
|
54
|
+
const metadata = { exit_code: '99' };
|
|
55
|
+
const exitCode = resolveInstanceExitCode(tempDir, metadata);
|
|
56
|
+
expect(exitCode).toBe(99);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should prefer /exit_code file over metadata', () => {
|
|
60
|
+
const metadata = { exit_code: 1 };
|
|
61
|
+
fs.writeFileSync(path.join(tempDir, 'exit_code'), '42');
|
|
62
|
+
|
|
63
|
+
const exitCode = resolveInstanceExitCode(tempDir, metadata);
|
|
64
|
+
expect(exitCode).toBe(42);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('should fall back to metadata if /exit_code file is invalid', () => {
|
|
68
|
+
const metadata = { exit_code: 1 };
|
|
69
|
+
fs.writeFileSync(path.join(tempDir, 'exit_code'), 'invalid');
|
|
70
|
+
|
|
71
|
+
const exitCode = resolveInstanceExitCode(tempDir, metadata);
|
|
72
|
+
expect(exitCode).toBe(1);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('should return null when no exit code is available', () => {
|
|
76
|
+
const exitCode = resolveInstanceExitCode(tempDir, {});
|
|
77
|
+
expect(exitCode).toBeNull();
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should treat malformed metadata exit codes as pending', () => {
|
|
81
|
+
const metadata = { exit_code: 1.5 };
|
|
82
|
+
|
|
83
|
+
const exitCode = resolveInstanceExitCode(tempDir, metadata);
|
|
84
|
+
const status = deriveInstanceLifecycleStatus(false, exitCode);
|
|
85
|
+
|
|
86
|
+
expect(exitCode).toBeNull();
|
|
87
|
+
expect(status).toBe('pending');
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('resolveInstanceStage', () => {
|
|
92
|
+
it('should return current_stage from metadata when available', () => {
|
|
93
|
+
const metadata = { current_stage: 'validation' };
|
|
94
|
+
const instanceDir = path.join(tempDir, 'kaseki-1');
|
|
95
|
+
fs.mkdirSync(instanceDir, { recursive: true });
|
|
96
|
+
|
|
97
|
+
const stage = resolveInstanceStage(tempDir, 'kaseki-1', metadata);
|
|
98
|
+
expect(stage).toBe('validation');
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it('should parse stage from stdout.log when metadata is missing', () => {
|
|
102
|
+
const instanceDir = path.join(tempDir, 'kaseki-1');
|
|
103
|
+
fs.mkdirSync(instanceDir, { recursive: true });
|
|
104
|
+
fs.writeFileSync(
|
|
105
|
+
path.join(instanceDir, 'stdout.log'),
|
|
106
|
+
'==> Clone repo\n==> Install dependencies\n==> pi coding agent'
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const stage = resolveInstanceStage(tempDir, 'kaseki-1', {});
|
|
110
|
+
expect(stage).toBe('pi coding agent');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should return fallback when no stage information is available', () => {
|
|
114
|
+
const instanceDir = path.join(tempDir, 'kaseki-1');
|
|
115
|
+
fs.mkdirSync(instanceDir, { recursive: true });
|
|
116
|
+
|
|
117
|
+
const stage = resolveInstanceStage(tempDir, 'kaseki-1', {}, 'default');
|
|
118
|
+
expect(stage).toBe('default');
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should ignore empty current_stage in metadata', () => {
|
|
122
|
+
const metadata = { current_stage: ' ' };
|
|
123
|
+
const instanceDir = path.join(tempDir, 'kaseki-1');
|
|
124
|
+
fs.mkdirSync(instanceDir, { recursive: true });
|
|
125
|
+
fs.writeFileSync(
|
|
126
|
+
path.join(instanceDir, 'stdout.log'),
|
|
127
|
+
'==> Stage from stdout'
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const stage = resolveInstanceStage(tempDir, 'kaseki-1', metadata);
|
|
131
|
+
expect(stage).toBe('Stage from stdout');
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
describe('classifyFailure', () => {
|
|
136
|
+
it('should classify zero exit code as none', () => {
|
|
137
|
+
const classification = classifyFailure({}, 0);
|
|
138
|
+
expect(classification).toBe('none');
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('should classify exit code 124 as timeout', () => {
|
|
142
|
+
const classification = classifyFailure({}, 124);
|
|
143
|
+
expect(classification).toBe('timeout');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it('should classify empty diff failures', () => {
|
|
147
|
+
const classification1 = classifyFailure({ failed_command: 'empty git diff' }, 3);
|
|
148
|
+
const classification2 = classifyFailure({}, 3);
|
|
149
|
+
expect(classification1).toBe('empty-diff');
|
|
150
|
+
expect(classification2).toBe('empty-diff');
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should classify validation failures', () => {
|
|
154
|
+
const classification = classifyFailure({ failed_command: 'validation' }, 1);
|
|
155
|
+
expect(classification).toBe('validation');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should classify quality gate failures', () => {
|
|
159
|
+
const classification = classifyFailure({ failed_command: 'quality checks' }, 5);
|
|
160
|
+
expect(classification).toBe('quality');
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('should classify secret scan failures', () => {
|
|
164
|
+
const classification = classifyFailure({ failed_command: 'secret scan' }, 6);
|
|
165
|
+
expect(classification).toBe('secret-scan');
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('should classify github-related failures', () => {
|
|
169
|
+
const classification = classifyFailure({ failed_command: 'github-api-error' }, 1);
|
|
170
|
+
expect(classification).toBe('github');
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
it('should classify credential-related failures', () => {
|
|
174
|
+
const c1 = classifyFailure({ failed_command: 'OPENROUTER_API_KEY missing' }, 1);
|
|
175
|
+
const c2 = classifyFailure({ failed_command: 'OpenRouter error' }, 1);
|
|
176
|
+
expect(c1).toBe('credentials');
|
|
177
|
+
expect(c2).toBe('credentials');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it('should return unknown for unclassified failures', () => {
|
|
181
|
+
const classification = classifyFailure({}, 42);
|
|
182
|
+
expect(classification).toBe('nonzero-exit');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should sanitize failed_command by replacing spaces with dashes', () => {
|
|
186
|
+
const classification = classifyFailure(
|
|
187
|
+
{ failed_command: 'npm test run' },
|
|
188
|
+
1
|
|
189
|
+
);
|
|
190
|
+
expect(classification).toBe('npm-test-run');
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should handle string exit codes', () => {
|
|
194
|
+
const classification = classifyFailure({}, '124');
|
|
195
|
+
expect(classification).toBe('timeout');
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('extractValidationFailureReason', () => {
|
|
200
|
+
it('should return null when validation_failure_reason is not set', () => {
|
|
201
|
+
const reason = extractValidationFailureReason({});
|
|
202
|
+
expect(reason).toBeNull();
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
it('should return the reason when validation_failure_reason is set', () => {
|
|
206
|
+
const reason = extractValidationFailureReason({
|
|
207
|
+
validation_failure_reason: 'validation_command_failed: npm run test (exit 1)',
|
|
208
|
+
});
|
|
209
|
+
expect(reason).toBe('validation_command_failed: npm run test (exit 1)');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
it('should trim whitespace from reason', () => {
|
|
213
|
+
const reason = extractValidationFailureReason({
|
|
214
|
+
validation_failure_reason: ' missing_npm_script: check ',
|
|
215
|
+
});
|
|
216
|
+
expect(reason).toBe('missing_npm_script: check');
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('should return null for empty reason string', () => {
|
|
220
|
+
const reason = extractValidationFailureReason({
|
|
221
|
+
validation_failure_reason: ' ',
|
|
222
|
+
});
|
|
223
|
+
expect(reason).toBeNull();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
describe('extractQualityFailureReason', () => {
|
|
228
|
+
it('should return null when quality_failure_reason is not set', () => {
|
|
229
|
+
const reason = extractQualityFailureReason({});
|
|
230
|
+
expect(reason).toBeNull();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should return the reason when quality_failure_reason is set', () => {
|
|
234
|
+
const reason = extractQualityFailureReason({
|
|
235
|
+
quality_failure_reason: "allowlist_check: file 'src/forbidden.ts' not in allowlist",
|
|
236
|
+
});
|
|
237
|
+
expect(reason).toBe(
|
|
238
|
+
"allowlist_check: file 'src/forbidden.ts' not in allowlist"
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should handle max_diff_bytes failure reason', () => {
|
|
243
|
+
const reason = extractQualityFailureReason({
|
|
244
|
+
quality_failure_reason: 'max_diff_bytes: 250000 bytes exceeds limit of 200000 bytes',
|
|
245
|
+
});
|
|
246
|
+
expect(reason).toBe('max_diff_bytes: 250000 bytes exceeds limit of 200000 bytes');
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
it('should trim whitespace from reason', () => {
|
|
250
|
+
const reason = extractQualityFailureReason({
|
|
251
|
+
quality_failure_reason: ' max_diff_bytes: overflow ',
|
|
252
|
+
});
|
|
253
|
+
expect(reason).toBe('max_diff_bytes: overflow');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('should return null for empty reason string', () => {
|
|
257
|
+
const reason = extractQualityFailureReason({
|
|
258
|
+
quality_failure_reason: '',
|
|
259
|
+
});
|
|
260
|
+
expect(reason).toBeNull();
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* instance-state-derivation.ts
|
|
3
|
+
*
|
|
4
|
+
* Encapsulates state derivation logic for kaseki instances.
|
|
5
|
+
* Derives lifecycle status, exit codes, stages, and failure classifications
|
|
6
|
+
* from metadata and instance artifacts.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
import { Metadata } from './instance-metadata-reader';
|
|
12
|
+
|
|
13
|
+
export type InstanceLifecycleStatus = 'running' | 'completed' | 'failed' | 'pending';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Normalize an exit code candidate into an integer or null.
|
|
17
|
+
*/
|
|
18
|
+
export function normalizeExitCodeCandidate(value: any): number | null {
|
|
19
|
+
if (typeof value === 'number' && Number.isInteger(value)) {
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
if (typeof value === 'string' && /^-?\d+$/.test(value.trim())) {
|
|
23
|
+
return parseInt(value.trim(), 10);
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Derive lifecycle status from running flag and exit code.
|
|
30
|
+
*/
|
|
31
|
+
export function deriveInstanceLifecycleStatus(
|
|
32
|
+
isRunning: boolean,
|
|
33
|
+
exitCode: number | null
|
|
34
|
+
): InstanceLifecycleStatus {
|
|
35
|
+
if (isRunning) return 'running';
|
|
36
|
+
if (exitCode === 0) return 'completed';
|
|
37
|
+
if (Number.isInteger(exitCode)) return 'failed';
|
|
38
|
+
return 'pending';
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Resolve exit code from metadata and optional /exit_code file.
|
|
43
|
+
* Prefers /exit_code file when readable, falls back to metadata.exit_code.
|
|
44
|
+
* Returns null only when neither source has a valid integer.
|
|
45
|
+
*/
|
|
46
|
+
export function resolveInstanceExitCode(
|
|
47
|
+
resultDir: string,
|
|
48
|
+
metadata: Metadata = {}
|
|
49
|
+
): number | null {
|
|
50
|
+
const metadataExitCode = normalizeExitCodeCandidate(metadata.exit_code);
|
|
51
|
+
const exitCodePath = path.join(resultDir, 'exit_code');
|
|
52
|
+
if (!fs.existsSync(exitCodePath)) {
|
|
53
|
+
return metadataExitCode;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
const fileExitCode = normalizeExitCodeCandidate(fs.readFileSync(exitCodePath, 'utf8'));
|
|
58
|
+
return fileExitCode !== null ? fileExitCode : metadataExitCode;
|
|
59
|
+
} catch {
|
|
60
|
+
return metadataExitCode;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get the current stage of a running or completed instance.
|
|
66
|
+
* Parses stdout.log for "==> stage_name" markers.
|
|
67
|
+
*/
|
|
68
|
+
function getCurrentStage(resultsDir: string, instanceName: string): string {
|
|
69
|
+
const stdoutPath = path.join(resultsDir, instanceName, 'stdout.log');
|
|
70
|
+
if (!fs.existsSync(stdoutPath)) {
|
|
71
|
+
return 'unknown';
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
const stdout = fs.readFileSync(stdoutPath, 'utf8');
|
|
76
|
+
const matches = stdout.match(/^==> (.+?)$/gm);
|
|
77
|
+
if (!matches || matches.length === 0) return 'unknown';
|
|
78
|
+
|
|
79
|
+
const lastMarker = matches[matches.length - 1];
|
|
80
|
+
return lastMarker.replace(/^==> /, '').trim();
|
|
81
|
+
} catch {
|
|
82
|
+
return 'unknown';
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Resolve stage from metadata first, then fallback to stdout markers.
|
|
88
|
+
*/
|
|
89
|
+
export function resolveInstanceStage(
|
|
90
|
+
resultsDir: string,
|
|
91
|
+
instanceName: string,
|
|
92
|
+
metadata: Metadata = {},
|
|
93
|
+
fallback: string = 'unknown'
|
|
94
|
+
): string {
|
|
95
|
+
if (typeof metadata.current_stage === 'string' && metadata.current_stage.trim().length > 0) {
|
|
96
|
+
return metadata.current_stage;
|
|
97
|
+
}
|
|
98
|
+
const parsedStage = getCurrentStage(resultsDir, instanceName);
|
|
99
|
+
return parsedStage !== 'unknown' ? parsedStage : fallback;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Extract validation failure reason from metadata.
|
|
104
|
+
* Returns the reason if validation failed, otherwise null.
|
|
105
|
+
*/
|
|
106
|
+
export function extractValidationFailureReason(metadata: Metadata = {}): string | null {
|
|
107
|
+
const reason = typeof metadata.validation_failure_reason === 'string'
|
|
108
|
+
? metadata.validation_failure_reason.trim()
|
|
109
|
+
: '';
|
|
110
|
+
return reason.length > 0 ? reason : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Extract quality gate failure reason from metadata.
|
|
115
|
+
* Returns the reason if quality checks failed, otherwise null.
|
|
116
|
+
*/
|
|
117
|
+
export function extractQualityFailureReason(metadata: Metadata = {}): string | null {
|
|
118
|
+
const reason = typeof metadata.quality_failure_reason === 'string'
|
|
119
|
+
? metadata.quality_failure_reason.trim()
|
|
120
|
+
: '';
|
|
121
|
+
return reason.length > 0 ? reason : null;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Classify failure type from metadata and exit code.
|
|
126
|
+
*/
|
|
127
|
+
export function classifyFailure(
|
|
128
|
+
metadata: Metadata = {},
|
|
129
|
+
exitCode: number | string | null = null
|
|
130
|
+
): string {
|
|
131
|
+
const normalizedExitCode = normalizeExitCodeCandidate(exitCode);
|
|
132
|
+
const failedCommand =
|
|
133
|
+
typeof metadata.failed_command === 'string' ? metadata.failed_command.trim() : '';
|
|
134
|
+
|
|
135
|
+
if (normalizedExitCode === 0) return 'none';
|
|
136
|
+
if (normalizedExitCode === 124) return 'timeout';
|
|
137
|
+
if (failedCommand === 'empty git diff' || normalizedExitCode === 3) return 'empty-diff';
|
|
138
|
+
if (failedCommand === 'validation') return 'validation';
|
|
139
|
+
if (failedCommand === 'quality checks') return 'quality';
|
|
140
|
+
if (failedCommand === 'secret scan') return 'secret-scan';
|
|
141
|
+
if (failedCommand.startsWith('github')) return 'github';
|
|
142
|
+
if (failedCommand.includes('OPENROUTER_API_KEY') || failedCommand.includes('OpenRouter')) {
|
|
143
|
+
return 'credentials';
|
|
144
|
+
}
|
|
145
|
+
if (failedCommand) return failedCommand.replace(/\s+/g, '-');
|
|
146
|
+
if (Number.isInteger(normalizedExitCode)) return 'nonzero-exit';
|
|
147
|
+
return 'unknown';
|
|
148
|
+
}
|