@oscharko-dev/keiko 0.1.0-beta.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 +202 -0
- package/NOTICE +7 -0
- package/README.md +621 -0
- package/TRADEMARKS.md +41 -0
- package/dist/audit/aggregate.d.ts +5 -0
- package/dist/audit/aggregate.js +25 -0
- package/dist/audit/build.d.ts +2 -0
- package/dist/audit/build.js +224 -0
- package/dist/audit/errors.d.ts +25 -0
- package/dist/audit/errors.js +39 -0
- package/dist/audit/index-api.d.ts +14 -0
- package/dist/audit/index-api.js +131 -0
- package/dist/audit/index.d.ts +12 -0
- package/dist/audit/index.js +17 -0
- package/dist/audit/persist.d.ts +8 -0
- package/dist/audit/persist.js +40 -0
- package/dist/audit/redaction.d.ts +3 -0
- package/dist/audit/redaction.js +61 -0
- package/dist/audit/report.d.ts +18 -0
- package/dist/audit/report.js +50 -0
- package/dist/audit/retention.d.ts +3 -0
- package/dist/audit/retention.js +95 -0
- package/dist/audit/runid.d.ts +1 -0
- package/dist/audit/runid.js +29 -0
- package/dist/audit/side-file.d.ts +12 -0
- package/dist/audit/side-file.js +82 -0
- package/dist/audit/store.d.ts +12 -0
- package/dist/audit/store.js +198 -0
- package/dist/audit/types.d.ts +188 -0
- package/dist/audit/types.js +8 -0
- package/dist/audit/workflow-evidence.d.ts +27 -0
- package/dist/audit/workflow-evidence.js +145 -0
- package/dist/cli/context.d.ts +2 -0
- package/dist/cli/context.js +102 -0
- package/dist/cli/evaluate.d.ts +7 -0
- package/dist/cli/evaluate.js +207 -0
- package/dist/cli/evidence.d.ts +8 -0
- package/dist/cli/evidence.js +88 -0
- package/dist/cli/gateway-config.d.ts +10 -0
- package/dist/cli/gateway-config.js +12 -0
- package/dist/cli/gen-tests.d.ts +7 -0
- package/dist/cli/gen-tests.js +208 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +14 -0
- package/dist/cli/investigate.d.ts +8 -0
- package/dist/cli/investigate.js +242 -0
- package/dist/cli/models.d.ts +3 -0
- package/dist/cli/models.js +64 -0
- package/dist/cli/run.d.ts +7 -0
- package/dist/cli/run.js +187 -0
- package/dist/cli/runner.d.ts +6 -0
- package/dist/cli/runner.js +83 -0
- package/dist/cli/ui.d.ts +31 -0
- package/dist/cli/ui.js +240 -0
- package/dist/cli/verify.d.ts +2 -0
- package/dist/cli/verify.js +103 -0
- package/dist/evaluations/fixtures/bug-investigation/happy-path.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/happy-path.js +66 -0
- package/dist/evaluations/fixtures/bug-investigation/investigation-only.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/investigation-only.js +39 -0
- package/dist/evaluations/fixtures/bug-investigation/unsafe-action.d.ts +2 -0
- package/dist/evaluations/fixtures/bug-investigation/unsafe-action.js +37 -0
- package/dist/evaluations/fixtures/index.d.ts +7 -0
- package/dist/evaluations/fixtures/index.js +35 -0
- package/dist/evaluations/fixtures/support.d.ts +5 -0
- package/dist/evaluations/fixtures/support.js +42 -0
- package/dist/evaluations/fixtures/unit-tests/happy-path.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/happy-path.js +40 -0
- package/dist/evaluations/fixtures/unit-tests/retry-then-accept.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/retry-then-accept.js +39 -0
- package/dist/evaluations/fixtures/unit-tests/unsafe-action.d.ts +2 -0
- package/dist/evaluations/fixtures/unit-tests/unsafe-action.js +32 -0
- package/dist/evaluations/index.d.ts +12 -0
- package/dist/evaluations/index.js +12 -0
- package/dist/evaluations/manifest-check.d.ts +1 -0
- package/dist/evaluations/manifest-check.js +48 -0
- package/dist/evaluations/model-provider.d.ts +12 -0
- package/dist/evaluations/model-provider.js +26 -0
- package/dist/evaluations/render.d.ts +2 -0
- package/dist/evaluations/render.js +59 -0
- package/dist/evaluations/runner-support.d.ts +27 -0
- package/dist/evaluations/runner-support.js +163 -0
- package/dist/evaluations/runner.d.ts +20 -0
- package/dist/evaluations/runner.js +174 -0
- package/dist/evaluations/scorer.d.ts +14 -0
- package/dist/evaluations/scorer.js +131 -0
- package/dist/evaluations/scripted-model.d.ts +6 -0
- package/dist/evaluations/scripted-model.js +26 -0
- package/dist/evaluations/surface-parity.d.ts +2 -0
- package/dist/evaluations/surface-parity.js +184 -0
- package/dist/evaluations/types.d.ts +74 -0
- package/dist/evaluations/types.js +16 -0
- package/dist/gateway/capabilities.d.ts +11 -0
- package/dist/gateway/capabilities.data.d.ts +2 -0
- package/dist/gateway/capabilities.data.js +203 -0
- package/dist/gateway/capabilities.js +41 -0
- package/dist/gateway/config.d.ts +15 -0
- package/dist/gateway/config.js +154 -0
- package/dist/gateway/errors.d.ts +72 -0
- package/dist/gateway/errors.js +82 -0
- package/dist/gateway/gateway.d.ts +19 -0
- package/dist/gateway/gateway.js +94 -0
- package/dist/gateway/index.d.ts +10 -0
- package/dist/gateway/index.js +11 -0
- package/dist/gateway/model-selection.d.ts +9 -0
- package/dist/gateway/model-selection.js +36 -0
- package/dist/gateway/normalize.d.ts +7 -0
- package/dist/gateway/normalize.js +93 -0
- package/dist/gateway/openai-adapter.d.ts +20 -0
- package/dist/gateway/openai-adapter.js +263 -0
- package/dist/gateway/redaction.d.ts +1 -0
- package/dist/gateway/redaction.js +51 -0
- package/dist/gateway/resilience.d.ts +24 -0
- package/dist/gateway/resilience.js +166 -0
- package/dist/gateway/types.d.ts +108 -0
- package/dist/gateway/types.js +2 -0
- package/dist/harness/adapters.d.ts +23 -0
- package/dist/harness/adapters.js +38 -0
- package/dist/harness/context.d.ts +33 -0
- package/dist/harness/context.js +21 -0
- package/dist/harness/emitter.d.ts +15 -0
- package/dist/harness/emitter.js +72 -0
- package/dist/harness/errors.d.ts +21 -0
- package/dist/harness/errors.js +39 -0
- package/dist/harness/executor.d.ts +3 -0
- package/dist/harness/executor.js +211 -0
- package/dist/harness/fingerprint.d.ts +6 -0
- package/dist/harness/fingerprint.js +43 -0
- package/dist/harness/index.d.ts +9 -0
- package/dist/harness/index.js +13 -0
- package/dist/harness/loop.d.ts +3 -0
- package/dist/harness/loop.js +159 -0
- package/dist/harness/patcher.d.ts +4 -0
- package/dist/harness/patcher.js +49 -0
- package/dist/harness/planner.d.ts +3 -0
- package/dist/harness/planner.js +21 -0
- package/dist/harness/ports.d.ts +61 -0
- package/dist/harness/ports.js +4 -0
- package/dist/harness/session.d.ts +25 -0
- package/dist/harness/session.js +116 -0
- package/dist/harness/sinks.d.ts +30 -0
- package/dist/harness/sinks.js +72 -0
- package/dist/harness/tasks/explain-plan.d.ts +3 -0
- package/dist/harness/tasks/explain-plan.js +29 -0
- package/dist/harness/tasks/generate-unit-tests.d.ts +3 -0
- package/dist/harness/tasks/generate-unit-tests.js +28 -0
- package/dist/harness/tasks/investigate-bug.d.ts +3 -0
- package/dist/harness/tasks/investigate-bug.js +31 -0
- package/dist/harness/tasks/policy.d.ts +11 -0
- package/dist/harness/tasks/policy.js +22 -0
- package/dist/harness/tasks/verify.d.ts +3 -0
- package/dist/harness/tasks/verify.js +16 -0
- package/dist/harness/types.d.ts +270 -0
- package/dist/harness/types.js +33 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +36 -0
- package/dist/sdk/index.d.ts +9 -0
- package/dist/sdk/index.js +37 -0
- package/dist/sdk/run-agent.d.ts +16 -0
- package/dist/sdk/run-agent.js +56 -0
- package/dist/tools/browser/cdp-client.d.ts +35 -0
- package/dist/tools/browser/cdp-client.js +218 -0
- package/dist/tools/browser/errors.d.ts +25 -0
- package/dist/tools/browser/errors.js +55 -0
- package/dist/tools/browser/index.d.ts +5 -0
- package/dist/tools/browser/index.js +6 -0
- package/dist/tools/browser/session.d.ts +44 -0
- package/dist/tools/browser/session.js +748 -0
- package/dist/tools/browser/types.d.ts +48 -0
- package/dist/tools/browser/types.js +2 -0
- package/dist/tools/browser/validators.d.ts +5 -0
- package/dist/tools/browser/validators.js +97 -0
- package/dist/tools/errors.d.ts +59 -0
- package/dist/tools/errors.js +94 -0
- package/dist/tools/exec.d.ts +42 -0
- package/dist/tools/exec.js +327 -0
- package/dist/tools/index.d.ts +11 -0
- package/dist/tools/index.js +14 -0
- package/dist/tools/patch-content.d.ts +10 -0
- package/dist/tools/patch-content.js +126 -0
- package/dist/tools/patch-normalize.d.ts +1 -0
- package/dist/tools/patch-normalize.js +80 -0
- package/dist/tools/patch-parse.d.ts +8 -0
- package/dist/tools/patch-parse.js +201 -0
- package/dist/tools/patch.d.ts +18 -0
- package/dist/tools/patch.js +403 -0
- package/dist/tools/registry.d.ts +36 -0
- package/dist/tools/registry.js +231 -0
- package/dist/tools/sandbox.d.ts +8 -0
- package/dist/tools/sandbox.js +121 -0
- package/dist/tools/schemas.d.ts +2 -0
- package/dist/tools/schemas.js +51 -0
- package/dist/tools/terminal-policy.d.ts +9 -0
- package/dist/tools/terminal-policy.js +313 -0
- package/dist/tools/types.d.ts +99 -0
- package/dist/tools/types.js +103 -0
- package/dist/tools/writer.d.ts +7 -0
- package/dist/tools/writer.js +20 -0
- package/dist/ui/browser.d.ts +10 -0
- package/dist/ui/browser.js +231 -0
- package/dist/ui/chat-handlers.d.ts +4 -0
- package/dist/ui/chat-handlers.js +281 -0
- package/dist/ui/csp-hashes.json +17 -0
- package/dist/ui/csp.d.ts +2 -0
- package/dist/ui/csp.js +66 -0
- package/dist/ui/deps.d.ts +34 -0
- package/dist/ui/deps.js +137 -0
- package/dist/ui/evidence.d.ts +27 -0
- package/dist/ui/evidence.js +142 -0
- package/dist/ui/files-deny.d.ts +2 -0
- package/dist/ui/files-deny.js +12 -0
- package/dist/ui/files.d.ts +65 -0
- package/dist/ui/files.js +492 -0
- package/dist/ui/headers.d.ts +2 -0
- package/dist/ui/headers.js +21 -0
- package/dist/ui/host-check.d.ts +2 -0
- package/dist/ui/host-check.js +58 -0
- package/dist/ui/index.d.ts +20 -0
- package/dist/ui/index.js +23 -0
- package/dist/ui/load-csp.d.ts +1 -0
- package/dist/ui/load-csp.js +28 -0
- package/dist/ui/read-handlers.d.ts +8 -0
- package/dist/ui/read-handlers.js +247 -0
- package/dist/ui/routes.d.ts +36 -0
- package/dist/ui/routes.js +129 -0
- package/dist/ui/run-engine.d.ts +20 -0
- package/dist/ui/run-engine.js +345 -0
- package/dist/ui/run-handlers.d.ts +8 -0
- package/dist/ui/run-handlers.js +431 -0
- package/dist/ui/run-request.d.ts +13 -0
- package/dist/ui/run-request.js +219 -0
- package/dist/ui/runs.d.ts +43 -0
- package/dist/ui/runs.js +92 -0
- package/dist/ui/server.d.ts +11 -0
- package/dist/ui/server.js +143 -0
- package/dist/ui/sink.d.ts +27 -0
- package/dist/ui/sink.js +80 -0
- package/dist/ui/sse.d.ts +7 -0
- package/dist/ui/sse.js +27 -0
- package/dist/ui/static/404.html +1 -0
- package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_buildManifest.js +1 -0
- package/dist/ui/static/_next/static/ca-A01hy9W98aRvMZKdAw/_ssgManifest.js +1 -0
- package/dist/ui/static/_next/static/chunks/255-d47fd57964443afe.js +1 -0
- package/dist/ui/static/_next/static/chunks/4-be1fef693af8e088.js +1 -0
- package/dist/ui/static/_next/static/chunks/4bd1b696-c023c6e3521b1417.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/_not-found/page-75825b09bcecad97.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/launch/page-9c86a13c29884245.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/layout-bdea63fe87947d50.js +1 -0
- package/dist/ui/static/_next/static/chunks/app/page-4168c12c68b7a853.js +1 -0
- package/dist/ui/static/_next/static/chunks/framework-a6e0b7e30f98059a.js +1 -0
- package/dist/ui/static/_next/static/chunks/main-778a50aebff02192.js +1 -0
- package/dist/ui/static/_next/static/chunks/main-app-30679af7240d63e9.js +1 -0
- package/dist/ui/static/_next/static/chunks/pages/_app-7d307437aca18ad4.js +1 -0
- package/dist/ui/static/_next/static/chunks/pages/_error-cb2a52f75f2162e2.js +1 -0
- package/dist/ui/static/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/ui/static/_next/static/chunks/webpack-4a462cecab786e93.js +1 -0
- package/dist/ui/static/_next/static/css/be7cb54d5c5673b6.css +1 -0
- package/dist/ui/static/assets/editors/goland.svg +35 -0
- package/dist/ui/static/assets/editors/intellij.svg +39 -0
- package/dist/ui/static/assets/editors/pycharm.svg +58 -0
- package/dist/ui/static/assets/editors/rustrover.svg +19 -0
- package/dist/ui/static/assets/editors/vscode.svg +1 -0
- package/dist/ui/static/assets/editors/webstorm.svg +21 -0
- package/dist/ui/static/assets/icons/anthropic.svg +1 -0
- package/dist/ui/static/assets/icons/brave.svg +1 -0
- package/dist/ui/static/assets/icons/css3.svg +1 -0
- package/dist/ui/static/assets/icons/docker.svg +1 -0
- package/dist/ui/static/assets/icons/git.svg +1 -0
- package/dist/ui/static/assets/icons/github.svg +1 -0
- package/dist/ui/static/assets/icons/go.svg +1 -0
- package/dist/ui/static/assets/icons/gradle.svg +1 -0
- package/dist/ui/static/assets/icons/grafana.svg +1 -0
- package/dist/ui/static/assets/icons/graphql.svg +1 -0
- package/dist/ui/static/assets/icons/html5.svg +1 -0
- package/dist/ui/static/assets/icons/image.svg +1 -0
- package/dist/ui/static/assets/icons/java.svg +1 -0
- package/dist/ui/static/assets/icons/javascript.svg +1 -0
- package/dist/ui/static/assets/icons/json.svg +1 -0
- package/dist/ui/static/assets/icons/kafka.svg +1 -0
- package/dist/ui/static/assets/icons/kubernetes.svg +1 -0
- package/dist/ui/static/assets/icons/linear.svg +1 -0
- package/dist/ui/static/assets/icons/markdown.svg +1 -0
- package/dist/ui/static/assets/icons/nginx.svg +1 -0
- package/dist/ui/static/assets/icons/nodejs.svg +1 -0
- package/dist/ui/static/assets/icons/notion.svg +1 -0
- package/dist/ui/static/assets/icons/openai.svg +1 -0
- package/dist/ui/static/assets/icons/playwright.svg +1 -0
- package/dist/ui/static/assets/icons/postgresql.svg +1 -0
- package/dist/ui/static/assets/icons/prometheus.svg +1 -0
- package/dist/ui/static/assets/icons/properties.svg +1 -0
- package/dist/ui/static/assets/icons/puppeteer.svg +1 -0
- package/dist/ui/static/assets/icons/python.svg +1 -0
- package/dist/ui/static/assets/icons/react.svg +1 -0
- package/dist/ui/static/assets/icons/redis.svg +1 -0
- package/dist/ui/static/assets/icons/rust.svg +1 -0
- package/dist/ui/static/assets/icons/sentry.svg +1 -0
- package/dist/ui/static/assets/icons/slack.svg +1 -0
- package/dist/ui/static/assets/icons/spring.svg +1 -0
- package/dist/ui/static/assets/icons/typescript.svg +1 -0
- package/dist/ui/static/assets/icons/upstash.svg +1 -0
- package/dist/ui/static/assets/icons/yaml.svg +1 -0
- package/dist/ui/static/assets/keiko-logo.svg +10 -0
- package/dist/ui/static/index.html +1 -0
- package/dist/ui/static/index.txt +19 -0
- package/dist/ui/static/keiko-logo.svg +10 -0
- package/dist/ui/static/launch.html +1 -0
- package/dist/ui/static/launch.txt +19 -0
- package/dist/ui/static.d.ts +3 -0
- package/dist/ui/static.js +72 -0
- package/dist/ui/store/chats.d.ts +14 -0
- package/dist/ui/store/chats.js +110 -0
- package/dist/ui/store/db.d.ts +6 -0
- package/dist/ui/store/db.js +182 -0
- package/dist/ui/store/errors.d.ts +12 -0
- package/dist/ui/store/errors.js +30 -0
- package/dist/ui/store/index.d.ts +6 -0
- package/dist/ui/store/index.js +6 -0
- package/dist/ui/store/messages.d.ts +5 -0
- package/dist/ui/store/messages.js +137 -0
- package/dist/ui/store/paths.d.ts +4 -0
- package/dist/ui/store/paths.js +69 -0
- package/dist/ui/store/projects.d.ts +7 -0
- package/dist/ui/store/projects.js +61 -0
- package/dist/ui/store/schema.d.ts +3 -0
- package/dist/ui/store/schema.js +77 -0
- package/dist/ui/store/types.d.ts +80 -0
- package/dist/ui/store/types.js +3 -0
- package/dist/ui/store/validation.d.ts +4 -0
- package/dist/ui/store/validation.js +72 -0
- package/dist/ui/store-handlers.d.ts +16 -0
- package/dist/ui/store-handlers.js +465 -0
- package/dist/ui/terminal-errors.d.ts +21 -0
- package/dist/ui/terminal-errors.js +45 -0
- package/dist/ui/terminal-evidence.d.ts +20 -0
- package/dist/ui/terminal-evidence.js +65 -0
- package/dist/ui/terminal-routes.d.ts +9 -0
- package/dist/ui/terminal-routes.js +219 -0
- package/dist/ui/terminal.d.ts +67 -0
- package/dist/ui/terminal.js +835 -0
- package/dist/verification/classify.d.ts +10 -0
- package/dist/verification/classify.js +53 -0
- package/dist/verification/detect.d.ts +4 -0
- package/dist/verification/detect.js +81 -0
- package/dist/verification/errors.d.ts +11 -0
- package/dist/verification/errors.js +21 -0
- package/dist/verification/index.d.ts +17 -0
- package/dist/verification/index.js +13 -0
- package/dist/verification/limits.d.ts +3 -0
- package/dist/verification/limits.js +40 -0
- package/dist/verification/monitor.d.ts +4 -0
- package/dist/verification/monitor.js +58 -0
- package/dist/verification/orchestrator.d.ts +16 -0
- package/dist/verification/orchestrator.js +363 -0
- package/dist/verification/plan.d.ts +9 -0
- package/dist/verification/plan.js +125 -0
- package/dist/verification/summary.d.ts +40 -0
- package/dist/verification/summary.js +67 -0
- package/dist/verification/types.d.ts +63 -0
- package/dist/verification/types.js +13 -0
- package/dist/workflows/bug-investigation/context.d.ts +7 -0
- package/dist/workflows/bug-investigation/context.js +119 -0
- package/dist/workflows/bug-investigation/descriptor.d.ts +3 -0
- package/dist/workflows/bug-investigation/descriptor.js +46 -0
- package/dist/workflows/bug-investigation/emit.d.ts +12 -0
- package/dist/workflows/bug-investigation/emit.js +35 -0
- package/dist/workflows/bug-investigation/events.d.ts +81 -0
- package/dist/workflows/bug-investigation/events.js +9 -0
- package/dist/workflows/bug-investigation/failure-parse.d.ts +3 -0
- package/dist/workflows/bug-investigation/failure-parse.js +154 -0
- package/dist/workflows/bug-investigation/guard.d.ts +2 -0
- package/dist/workflows/bug-investigation/guard.js +69 -0
- package/dist/workflows/bug-investigation/index.d.ts +7 -0
- package/dist/workflows/bug-investigation/index.js +13 -0
- package/dist/workflows/bug-investigation/internal.d.ts +37 -0
- package/dist/workflows/bug-investigation/internal.js +64 -0
- package/dist/workflows/bug-investigation/model-loop.d.ts +4 -0
- package/dist/workflows/bug-investigation/model-loop.js +223 -0
- package/dist/workflows/bug-investigation/parse.d.ts +3 -0
- package/dist/workflows/bug-investigation/parse.js +123 -0
- package/dist/workflows/bug-investigation/prompt.d.ts +4 -0
- package/dist/workflows/bug-investigation/prompt.js +107 -0
- package/dist/workflows/bug-investigation/report.d.ts +23 -0
- package/dist/workflows/bug-investigation/report.js +151 -0
- package/dist/workflows/bug-investigation/stages.d.ts +13 -0
- package/dist/workflows/bug-investigation/stages.js +242 -0
- package/dist/workflows/bug-investigation/types.d.ts +91 -0
- package/dist/workflows/bug-investigation/types.js +14 -0
- package/dist/workflows/bug-investigation/verify-stage.d.ts +10 -0
- package/dist/workflows/bug-investigation/verify-stage.js +91 -0
- package/dist/workflows/bug-investigation/workflow.d.ts +2 -0
- package/dist/workflows/bug-investigation/workflow.js +74 -0
- package/dist/workflows/descriptor.d.ts +20 -0
- package/dist/workflows/descriptor.js +8 -0
- package/dist/workflows/index.d.ts +3 -0
- package/dist/workflows/index.js +2 -0
- package/dist/workflows/unit-tests/context.d.ts +7 -0
- package/dist/workflows/unit-tests/context.js +129 -0
- package/dist/workflows/unit-tests/conventions.d.ts +4 -0
- package/dist/workflows/unit-tests/conventions.js +87 -0
- package/dist/workflows/unit-tests/descriptor.d.ts +4 -0
- package/dist/workflows/unit-tests/descriptor.js +43 -0
- package/dist/workflows/unit-tests/emit.d.ts +12 -0
- package/dist/workflows/unit-tests/emit.js +35 -0
- package/dist/workflows/unit-tests/events.d.ts +78 -0
- package/dist/workflows/unit-tests/events.js +7 -0
- package/dist/workflows/unit-tests/index.d.ts +6 -0
- package/dist/workflows/unit-tests/index.js +10 -0
- package/dist/workflows/unit-tests/internal.d.ts +35 -0
- package/dist/workflows/unit-tests/internal.js +43 -0
- package/dist/workflows/unit-tests/model-loop.d.ts +4 -0
- package/dist/workflows/unit-tests/model-loop.js +95 -0
- package/dist/workflows/unit-tests/parse.d.ts +6 -0
- package/dist/workflows/unit-tests/parse.js +68 -0
- package/dist/workflows/unit-tests/prompt.d.ts +4 -0
- package/dist/workflows/unit-tests/prompt.js +71 -0
- package/dist/workflows/unit-tests/report.d.ts +21 -0
- package/dist/workflows/unit-tests/report.js +90 -0
- package/dist/workflows/unit-tests/stages.d.ts +9 -0
- package/dist/workflows/unit-tests/stages.js +155 -0
- package/dist/workflows/unit-tests/types.d.ts +70 -0
- package/dist/workflows/unit-tests/types.js +11 -0
- package/dist/workflows/unit-tests/verify-stage.d.ts +9 -0
- package/dist/workflows/unit-tests/verify-stage.js +56 -0
- package/dist/workflows/unit-tests/workflow.d.ts +2 -0
- package/dist/workflows/unit-tests/workflow.js +58 -0
- package/dist/workspace/contextPack.d.ts +9 -0
- package/dist/workspace/contextPack.js +94 -0
- package/dist/workspace/detect.d.ts +3 -0
- package/dist/workspace/detect.js +135 -0
- package/dist/workspace/discovery.d.ts +9 -0
- package/dist/workspace/discovery.js +167 -0
- package/dist/workspace/errors.d.ts +39 -0
- package/dist/workspace/errors.js +66 -0
- package/dist/workspace/fs.d.ts +21 -0
- package/dist/workspace/fs.js +36 -0
- package/dist/workspace/ignore.d.ts +14 -0
- package/dist/workspace/ignore.js +176 -0
- package/dist/workspace/index.d.ts +11 -0
- package/dist/workspace/index.js +13 -0
- package/dist/workspace/paths.d.ts +2 -0
- package/dist/workspace/paths.js +38 -0
- package/dist/workspace/realpath.d.ts +7 -0
- package/dist/workspace/realpath.js +72 -0
- package/dist/workspace/retrieval.d.ts +9 -0
- package/dist/workspace/retrieval.js +74 -0
- package/dist/workspace/summary.d.ts +3 -0
- package/dist/workspace/summary.js +54 -0
- package/dist/workspace/types.d.ts +103 -0
- package/dist/workspace/types.js +27 -0
- package/package.json +58 -0
package/dist/cli/run.js
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// `keiko run` — the dry-run task command. It builds an AgentSession wired to deterministic mocked
|
|
2
|
+
// model/tool fixtures (no provider call, no real tools) and renders the HarnessEvent stream to
|
|
3
|
+
// CliIo. Since ADR-0010 it ALSO writes a redacted evidence manifest by default (evidence is the
|
|
4
|
+
// product value): a tee EventSink forwards every event to BOTH a MemoryEventSink (which retains raw
|
|
5
|
+
// content to assemble the replay manifest) and the existing CliEventSink (whose summarisers never
|
|
6
|
+
// print sensitive fields). After the run resolves, the audit layer builds + redacts + persists the
|
|
7
|
+
// manifest and the EvidenceReport is printed. Writing is on by default; --no-evidence disables it,
|
|
8
|
+
// --evidence-dir relocates it. Tests inject an in-memory EvidenceStore via deps so no write ever
|
|
9
|
+
// touches the repository tree.
|
|
10
|
+
import { DryRunToolPort } from "../harness/adapters.js";
|
|
11
|
+
import { createSession, HARNESS_VERSION } from "../harness/session.js";
|
|
12
|
+
import { CliEventSink, MemoryEventSink } from "../harness/sinks.js";
|
|
13
|
+
import { DEFAULT_LIMITS } from "../harness/types.js";
|
|
14
|
+
import { persistEvidence } from "../audit/persist.js";
|
|
15
|
+
import { renderEvidenceReport } from "../audit/report.js";
|
|
16
|
+
import { createNodeEvidenceStore, resolveEvidenceDir } from "../audit/store.js";
|
|
17
|
+
import { AuditError } from "../audit/errors.js";
|
|
18
|
+
import { redact } from "../gateway/redaction.js";
|
|
19
|
+
const TASK_TYPES = new Set([
|
|
20
|
+
"generate-unit-tests",
|
|
21
|
+
"investigate-bug",
|
|
22
|
+
"explain-plan",
|
|
23
|
+
]);
|
|
24
|
+
const USAGE = `Usage:
|
|
25
|
+
keiko run explain-plan --file PATH [--question TEXT]
|
|
26
|
+
keiko run generate-unit-tests --file PATH [--function NAME]
|
|
27
|
+
keiko run investigate-bug --description TEXT [--file PATH]
|
|
28
|
+
|
|
29
|
+
Evidence flags (a redacted manifest is written by default):
|
|
30
|
+
--no-evidence Do not write an evidence manifest.
|
|
31
|
+
--evidence-dir PATH Write evidence under PATH (default $KEIKO_EVIDENCE_DIR or ./.keiko/evidence).
|
|
32
|
+
--include-reasoning Include redacted reasoning entries in the manifest.
|
|
33
|
+
--include-diff Include the redacted proposed diff in the manifest.
|
|
34
|
+
|
|
35
|
+
All tasks run in dry-run mode: a patch is proposed as an event, never written to disk.
|
|
36
|
+
`;
|
|
37
|
+
function flag(args, name) {
|
|
38
|
+
const i = args.indexOf(name);
|
|
39
|
+
if (i === -1) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const value = args[i + 1];
|
|
43
|
+
return value === undefined || value.startsWith("--") ? undefined : value;
|
|
44
|
+
}
|
|
45
|
+
function parseEvidenceFlags(args) {
|
|
46
|
+
return {
|
|
47
|
+
write: !args.includes("--no-evidence"),
|
|
48
|
+
evidenceDirFlag: flag(args, "--evidence-dir"),
|
|
49
|
+
includeReasoning: args.includes("--include-reasoning"),
|
|
50
|
+
includeDiff: args.includes("--include-diff"),
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
// A deterministic, dependency-free canned final response. It never requests tools, so the dry-run
|
|
54
|
+
// path is reproducible regardless of task type.
|
|
55
|
+
function cannedResponse() {
|
|
56
|
+
return {
|
|
57
|
+
modelId: "mock-model",
|
|
58
|
+
content: "--- a/file\n+++ b/file\n+// dry-run proposed change\n",
|
|
59
|
+
finishReason: "stop",
|
|
60
|
+
toolCalls: [],
|
|
61
|
+
structuredOutput: null,
|
|
62
|
+
usage: {
|
|
63
|
+
requestId: "dry-run",
|
|
64
|
+
promptTokens: 0,
|
|
65
|
+
completionTokens: 0,
|
|
66
|
+
latencyMs: 1,
|
|
67
|
+
costClass: "low",
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
function parseTask(taskType, args) {
|
|
72
|
+
const file = flag(args, "--file");
|
|
73
|
+
if (taskType === "explain-plan") {
|
|
74
|
+
if (file === undefined) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
return { taskType, input: { filePath: file, question: flag(args, "--question") } };
|
|
78
|
+
}
|
|
79
|
+
if (taskType === "generate-unit-tests") {
|
|
80
|
+
if (file === undefined) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
return { taskType, input: { filePath: file, targetFunction: flag(args, "--function") } };
|
|
84
|
+
}
|
|
85
|
+
const description = flag(args, "--description");
|
|
86
|
+
if (description === undefined) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { taskType, input: { description, filePaths: file === undefined ? undefined : [file] } };
|
|
90
|
+
}
|
|
91
|
+
// Forwards each event to every wrapped sink. retainsRawContent is true so the harness emits raw
|
|
92
|
+
// SENSITIVE fields — required for the MemoryEventSink's faithful replay manifest. The CliEventSink
|
|
93
|
+
// summarisers never print those fields, and the audit layer redacts before anything is persisted.
|
|
94
|
+
function teeSink(sinks) {
|
|
95
|
+
return {
|
|
96
|
+
retainsRawContent: true,
|
|
97
|
+
emit: (event) => {
|
|
98
|
+
for (const sink of sinks) {
|
|
99
|
+
sink.emit(event);
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function seedFor(task, result) {
|
|
105
|
+
return {
|
|
106
|
+
runId: result.runId,
|
|
107
|
+
fingerprint: result.fingerprint,
|
|
108
|
+
harnessVersion: HARNESS_VERSION,
|
|
109
|
+
taskType: task.taskType,
|
|
110
|
+
taskInput: task,
|
|
111
|
+
limits: DEFAULT_LIMITS,
|
|
112
|
+
modelId: "mock-model",
|
|
113
|
+
workingDirectory: ".",
|
|
114
|
+
dryRun: true,
|
|
115
|
+
startedAt: new Date(result.startedAt).toISOString(),
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
// Persists the evidence manifest. This is a system boundary (filesystem write), so try/catch is
|
|
119
|
+
// correct here (CLAUDE.md): on any failure — typed AuditError or otherwise — print a REDACTED
|
|
120
|
+
// message and return exit 1 rather than rejecting out of runAgentCli as an unhandled rejection (C3).
|
|
121
|
+
// Returns undefined on success so the caller falls through to the run-outcome exit code.
|
|
122
|
+
function writeEvidence(result, memory, task, ctx, io) {
|
|
123
|
+
try {
|
|
124
|
+
const manifest = memory.collectManifest(seedFor(task, result));
|
|
125
|
+
const store = ctx.deps.store ??
|
|
126
|
+
createNodeEvidenceStore(resolveEvidenceDir(ctx.flags.evidenceDirFlag, ctx.env));
|
|
127
|
+
const out = persistEvidence({
|
|
128
|
+
result,
|
|
129
|
+
manifest,
|
|
130
|
+
options: {
|
|
131
|
+
includeReasoning: ctx.flags.includeReasoning,
|
|
132
|
+
includeDiff: ctx.flags.includeDiff,
|
|
133
|
+
},
|
|
134
|
+
}, { store, env: ctx.env });
|
|
135
|
+
io.out(renderEvidenceReport(out.report));
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
const detail = error instanceof AuditError ? error.message : redact(String(error));
|
|
140
|
+
io.err(`keiko run: failed to write evidence: ${detail}\n`);
|
|
141
|
+
return 1;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function outcomeToExitCode(result, io) {
|
|
145
|
+
if (result.outcome === "completed") {
|
|
146
|
+
io.out(`run ${result.runId} completed (fingerprint ${result.fingerprint})\n`);
|
|
147
|
+
return 0;
|
|
148
|
+
}
|
|
149
|
+
if (result.outcome === "cancelled") {
|
|
150
|
+
io.err(`run ${result.runId} cancelled\n`);
|
|
151
|
+
return 1;
|
|
152
|
+
}
|
|
153
|
+
const category = result.failure?.category ?? "HARNESS_INTERNAL";
|
|
154
|
+
io.err(`run ${result.runId} ${result.outcome} [${category}]: ${result.failure?.message ?? ""}\n`);
|
|
155
|
+
return 1;
|
|
156
|
+
}
|
|
157
|
+
export async function runAgentCli(args, io, env = {}, deps = {}) {
|
|
158
|
+
const taskType = args[0];
|
|
159
|
+
if (taskType === undefined || !TASK_TYPES.has(taskType)) {
|
|
160
|
+
io.err(taskType === undefined ? USAGE : `keiko run: unknown task type: ${taskType}\n${USAGE}`);
|
|
161
|
+
return 2;
|
|
162
|
+
}
|
|
163
|
+
const task = parseTask(taskType, args.slice(1));
|
|
164
|
+
if (task === null) {
|
|
165
|
+
io.err(`keiko run: missing required argument for ${taskType}.\n${USAGE}`);
|
|
166
|
+
return 2;
|
|
167
|
+
}
|
|
168
|
+
const flags = parseEvidenceFlags(args);
|
|
169
|
+
const response = cannedResponse();
|
|
170
|
+
const memory = new MemoryEventSink();
|
|
171
|
+
const config = { model: "mock-model", workingDirectory: ".", dryRun: true };
|
|
172
|
+
const session = createSession(task, config, {
|
|
173
|
+
model: {
|
|
174
|
+
call: (request) => Promise.resolve({ ...response, modelId: request.modelId }),
|
|
175
|
+
},
|
|
176
|
+
tools: new DryRunToolPort(),
|
|
177
|
+
sink: teeSink([memory, new CliEventSink(io)]),
|
|
178
|
+
});
|
|
179
|
+
const result = await session.result;
|
|
180
|
+
if (flags.write) {
|
|
181
|
+
const evidenceFailure = writeEvidence(result, memory, task, { flags, env, deps }, io);
|
|
182
|
+
if (evidenceFailure !== undefined) {
|
|
183
|
+
return evidenceFailure;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return outcomeToExitCode(result, io);
|
|
187
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { EnvSource } from "../gateway/config.js";
|
|
2
|
+
export interface CliIo {
|
|
3
|
+
readonly out: (text: string) => void;
|
|
4
|
+
readonly err: (text: string) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function runCli(args: readonly string[], io: CliIo, env?: EnvSource): number | Promise<number>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { runModelsCli } from "./models.js";
|
|
2
|
+
import { runAgentCli } from "./run.js";
|
|
3
|
+
import { runContextCli } from "./context.js";
|
|
4
|
+
import { runVerifyCli } from "./verify.js";
|
|
5
|
+
import { runGenTestsCli } from "./gen-tests.js";
|
|
6
|
+
import { runInvestigateCli } from "./investigate.js";
|
|
7
|
+
import { runEvidenceCli } from "./evidence.js";
|
|
8
|
+
import { runEvaluateCli } from "./evaluate.js";
|
|
9
|
+
import { runUiCli } from "./ui.js";
|
|
10
|
+
import { SDK_VERSION } from "../sdk/index.js";
|
|
11
|
+
const HELP_TEXT = `keiko ${SDK_VERSION}
|
|
12
|
+
Enterprise model-agnostic developer-assist coding agent.
|
|
13
|
+
|
|
14
|
+
Usage:
|
|
15
|
+
keiko [--help | -h] Print this help and exit.
|
|
16
|
+
keiko [--version | -v] Print the version and exit.
|
|
17
|
+
keiko models list List registered model capabilities.
|
|
18
|
+
keiko models validate Validate gateway configuration.
|
|
19
|
+
keiko run <task> Run a bounded dry-run task through the agent harness.
|
|
20
|
+
keiko context [OPTIONS] Print a redacted workspace context summary (dry-run).
|
|
21
|
+
keiko verify [OPTIONS] Run the project's gates and print a redacted evidence summary.
|
|
22
|
+
keiko gen-tests [OPTIONS] Generate a reviewable unit-test patch (dry-run by default).
|
|
23
|
+
keiko investigate [OPTIONS] Investigate a bug and propose a fix + regression test (dry-run by default).
|
|
24
|
+
keiko evidence <list|show> Inspect redacted evidence manifests written by \`keiko run\`.
|
|
25
|
+
keiko evaluate [OPTIONS] Run the evaluation harness (offline by default; --live for live model).
|
|
26
|
+
keiko ui [OPTIONS] Launch the local UI on 127.0.0.1 and print its URL.
|
|
27
|
+
|
|
28
|
+
Exit codes:
|
|
29
|
+
0 Success
|
|
30
|
+
1 Runtime error
|
|
31
|
+
2 Usage error
|
|
32
|
+
`;
|
|
33
|
+
// Dispatches named subcommands; returns undefined when the name is not recognised.
|
|
34
|
+
function dispatchCommand(name, rest, io, env) {
|
|
35
|
+
if (name === "models") {
|
|
36
|
+
return runModelsCli(rest, io, env);
|
|
37
|
+
}
|
|
38
|
+
if (name === "run") {
|
|
39
|
+
return runAgentCli(rest, io, env);
|
|
40
|
+
}
|
|
41
|
+
if (name === "context") {
|
|
42
|
+
return runContextCli(rest, io);
|
|
43
|
+
}
|
|
44
|
+
if (name === "verify") {
|
|
45
|
+
return runVerifyCli(rest, io);
|
|
46
|
+
}
|
|
47
|
+
if (name === "gen-tests") {
|
|
48
|
+
return runGenTestsCli(rest, io, env);
|
|
49
|
+
}
|
|
50
|
+
if (name === "investigate") {
|
|
51
|
+
return runInvestigateCli(rest, io, env);
|
|
52
|
+
}
|
|
53
|
+
if (name === "evidence") {
|
|
54
|
+
return runEvidenceCli(rest, io, { env });
|
|
55
|
+
}
|
|
56
|
+
if (name === "evaluate") {
|
|
57
|
+
return runEvaluateCli(rest, io, env, {});
|
|
58
|
+
}
|
|
59
|
+
if (name === "ui") {
|
|
60
|
+
return runUiCli(rest, io, env);
|
|
61
|
+
}
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
// Returns a number for synchronous commands; the async `run` command returns a Promise.
|
|
65
|
+
// The process shim in index.ts awaits the union before calling process.exit.
|
|
66
|
+
export function runCli(args, io, env = {}) {
|
|
67
|
+
const first = args[0];
|
|
68
|
+
if (first === undefined || first === "--help" || first === "-h") {
|
|
69
|
+
io.out(HELP_TEXT);
|
|
70
|
+
return 0;
|
|
71
|
+
}
|
|
72
|
+
if (first === "--version" || first === "-v") {
|
|
73
|
+
io.out(`keiko ${SDK_VERSION}\n`);
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
const result = dispatchCommand(first, args.slice(1), io, env);
|
|
77
|
+
if (result !== undefined) {
|
|
78
|
+
return result;
|
|
79
|
+
}
|
|
80
|
+
io.err(`keiko: unknown ${first.startsWith("-") ? "option" : "command"}: ${first}\n`);
|
|
81
|
+
io.err("Run `keiko --help` for usage.\n");
|
|
82
|
+
return 2;
|
|
83
|
+
}
|
package/dist/cli/ui.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
import { type SpawnOptions, type ChildProcess } from "node:child_process";
|
|
3
|
+
import { type UiHandlerDeps } from "../ui/index.js";
|
|
4
|
+
import type { EnvSource } from "../gateway/config.js";
|
|
5
|
+
import type { CliIo } from "./runner.js";
|
|
6
|
+
export interface UiCliArgs {
|
|
7
|
+
readonly port: number;
|
|
8
|
+
readonly evidenceDir: string | undefined;
|
|
9
|
+
readonly config: string | undefined;
|
|
10
|
+
readonly uiDbPath: string | undefined;
|
|
11
|
+
}
|
|
12
|
+
export interface UiCliDeps {
|
|
13
|
+
readonly createServer?: (deps: {
|
|
14
|
+
staticRoot: string;
|
|
15
|
+
csp: string;
|
|
16
|
+
port: number;
|
|
17
|
+
handlerDeps: UiHandlerDeps;
|
|
18
|
+
}) => Server | Promise<Server>;
|
|
19
|
+
readonly staticRoot?: string;
|
|
20
|
+
readonly hashesFile?: string;
|
|
21
|
+
readonly sqliteProbe?: () => boolean;
|
|
22
|
+
readonly spawnFn?: SpawnFn;
|
|
23
|
+
readonly currentExecArgv?: () => readonly string[];
|
|
24
|
+
readonly cwd?: string | undefined;
|
|
25
|
+
}
|
|
26
|
+
export type SpawnFn = (command: string, args: readonly string[], opts: SpawnOptions) => ChildProcess;
|
|
27
|
+
export declare function parseUiArgs(args: readonly string[]): UiCliArgs | null;
|
|
28
|
+
export declare function applyServerTimeouts(server: Server): void;
|
|
29
|
+
export declare function waitForShutdown(server: Server): Promise<void>;
|
|
30
|
+
export declare function reExecWithSqliteFlag(_env: EnvSource, spawnFn: SpawnFn): Promise<number>;
|
|
31
|
+
export declare function runUiCli(args: readonly string[], io: CliIo, env: EnvSource, deps?: UiCliDeps): Promise<number>;
|
package/dist/cli/ui.js
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
// `keiko ui` — launches the local Wave 1 UI BFF (ADR-0011). It resolves the packaged static export
|
|
2
|
+
// (dist/ui/static) and the precomputed CSP hashes (dist/ui/csp-hashes.json) relative to this
|
|
3
|
+
// compiled module, builds the CSP, starts createUiServer bound to 127.0.0.1 only, prints the local
|
|
4
|
+
// URL, and keeps running until the process is signalled. Exit 2 on a usage error (bad --host/--port
|
|
5
|
+
// or a flag missing its value), 1 when the static export is not present (the package was built with
|
|
6
|
+
// `npm run build` but not `npm run build:ui`).
|
|
7
|
+
//
|
|
8
|
+
// ADR-0013 D2 site 1 — Detect-and-re-exec guard. Node 22.0–22.11 builds require
|
|
9
|
+
// --experimental-sqlite to import node:sqlite; 22.22+ loads it without the flag. The guard tries
|
|
10
|
+
// the import; on failure it re-spawns the current process with --experimental-sqlite prepended,
|
|
11
|
+
// inheriting stdio and forwarding SIGINT/SIGTERM to the child, then propagates the child's exit
|
|
12
|
+
// code. Injected-test invocations skip the guard entirely.
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { dirname, join, resolve } from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { spawn } from "node:child_process";
|
|
18
|
+
import { createUiServer, loadCspHeader, buildUiHandlerDeps, DEFAULT_UI_PORT, UI_HOST, } from "../ui/index.js";
|
|
19
|
+
const ALLOWED_HOSTS = new Set(["127.0.0.1", "localhost"]);
|
|
20
|
+
const SQLITE_FLAG = "--experimental-sqlite";
|
|
21
|
+
const KEIKO_ENV_NAME_RE = /^KEIKO_[A-Z0-9_]+$/;
|
|
22
|
+
const USAGE = `Usage:
|
|
23
|
+
keiko ui [--port PORT] [--host 127.0.0.1|localhost] [--evidence-dir PATH] [--config PATH] [--ui-db PATH]
|
|
24
|
+
|
|
25
|
+
Launches the local Keiko UI on the loopback interface and prints its URL. The server
|
|
26
|
+
binds 127.0.0.1 only and serves the packaged UI assets (built with \`npm run build:ui\`).
|
|
27
|
+
`;
|
|
28
|
+
function flagValue(args, name) {
|
|
29
|
+
const i = args.indexOf(name);
|
|
30
|
+
if (i === -1) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
const value = args[i + 1];
|
|
34
|
+
return value === undefined || value.startsWith("--") ? null : value;
|
|
35
|
+
}
|
|
36
|
+
function parsePort(raw) {
|
|
37
|
+
if (!/^\d{1,5}$/.test(raw)) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const port = Number(raw);
|
|
41
|
+
return port >= 1 && port <= 65535 ? port : null;
|
|
42
|
+
}
|
|
43
|
+
// Parses flags. Returns the parsed args, or null on any usage error (missing flag value, invalid
|
|
44
|
+
// port, or a host other than the two loopback names).
|
|
45
|
+
export function parseUiArgs(args) {
|
|
46
|
+
const portRaw = flagValue(args, "--port");
|
|
47
|
+
const hostRaw = flagValue(args, "--host");
|
|
48
|
+
const evidenceRaw = flagValue(args, "--evidence-dir");
|
|
49
|
+
const configRaw = flagValue(args, "--config");
|
|
50
|
+
const uiDbRaw = flagValue(args, "--ui-db");
|
|
51
|
+
if (portRaw === null ||
|
|
52
|
+
hostRaw === null ||
|
|
53
|
+
evidenceRaw === null ||
|
|
54
|
+
configRaw === null ||
|
|
55
|
+
uiDbRaw === null) {
|
|
56
|
+
return null;
|
|
57
|
+
}
|
|
58
|
+
if (hostRaw !== undefined && !ALLOWED_HOSTS.has(hostRaw)) {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const port = portRaw === undefined ? DEFAULT_UI_PORT : parsePort(portRaw);
|
|
62
|
+
if (port === null) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return { port, evidenceDir: evidenceRaw, config: configRaw, uiDbPath: uiDbRaw };
|
|
66
|
+
}
|
|
67
|
+
function defaultStaticRoot() {
|
|
68
|
+
return resolve(dirname(fileURLToPath(import.meta.url)), "..", "ui", "static");
|
|
69
|
+
}
|
|
70
|
+
function parseEnvValue(raw) {
|
|
71
|
+
const value = raw.trim();
|
|
72
|
+
if (value.length >= 2) {
|
|
73
|
+
const first = value[0];
|
|
74
|
+
const last = value[value.length - 1];
|
|
75
|
+
if ((first === `"` && last === `"`) || (first === "'" && last === "'")) {
|
|
76
|
+
return value.slice(1, -1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
function loadLocalKeikoEnv(cwd, env) {
|
|
82
|
+
const file = join(cwd, ".env");
|
|
83
|
+
if (!existsSync(file)) {
|
|
84
|
+
return env;
|
|
85
|
+
}
|
|
86
|
+
const merged = { ...env };
|
|
87
|
+
const text = readFileSync(file, "utf8");
|
|
88
|
+
for (const rawLine of text.split(/\r?\n/)) {
|
|
89
|
+
const line = rawLine.trim();
|
|
90
|
+
if (line.length === 0 || line.startsWith("#"))
|
|
91
|
+
continue;
|
|
92
|
+
const equals = line.indexOf("=");
|
|
93
|
+
if (equals <= 0)
|
|
94
|
+
continue;
|
|
95
|
+
const key = line.slice(0, equals).trim();
|
|
96
|
+
if (!KEIKO_ENV_NAME_RE.test(key))
|
|
97
|
+
continue;
|
|
98
|
+
if (merged[key] !== undefined)
|
|
99
|
+
continue;
|
|
100
|
+
merged[key] = parseEnvValue(line.slice(equals + 1));
|
|
101
|
+
}
|
|
102
|
+
return merged;
|
|
103
|
+
}
|
|
104
|
+
function resolveUiConfigPath(parsed, env) {
|
|
105
|
+
return parsed.config ?? env.KEIKO_CONFIG_FILE;
|
|
106
|
+
}
|
|
107
|
+
// Conservative request/header timeouts on the loopback BFF (defense in depth, L2/L3): even on
|
|
108
|
+
// 127.0.0.1 a slow or stuck client must not hold a connection indefinitely. headersTimeout must be
|
|
109
|
+
// at or below requestTimeout so an incomplete request line/header set is cut first.
|
|
110
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
111
|
+
const HEADERS_TIMEOUT_MS = 10_000;
|
|
112
|
+
export function applyServerTimeouts(server) {
|
|
113
|
+
server.requestTimeout = REQUEST_TIMEOUT_MS;
|
|
114
|
+
server.headersTimeout = HEADERS_TIMEOUT_MS;
|
|
115
|
+
}
|
|
116
|
+
async function listen(server, port) {
|
|
117
|
+
await new Promise((res, rej) => {
|
|
118
|
+
server.once("error", rej);
|
|
119
|
+
server.listen(port, UI_HOST, res);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// Keeps the real-CLI process alive until a shutdown signal or server close. Resolves cleanly so
|
|
123
|
+
// the caller can return 0. Registered listeners are removed on resolve to prevent leaks.
|
|
124
|
+
export function waitForShutdown(server) {
|
|
125
|
+
return new Promise((resolve) => {
|
|
126
|
+
const onClose = () => {
|
|
127
|
+
process.removeListener("SIGINT", onSignal);
|
|
128
|
+
process.removeListener("SIGTERM", onSignal);
|
|
129
|
+
resolve();
|
|
130
|
+
};
|
|
131
|
+
const onSignal = () => {
|
|
132
|
+
server.removeListener("close", onClose);
|
|
133
|
+
server.close(() => {
|
|
134
|
+
resolve();
|
|
135
|
+
});
|
|
136
|
+
};
|
|
137
|
+
server.once("close", onClose);
|
|
138
|
+
process.once("SIGINT", onSignal);
|
|
139
|
+
process.once("SIGTERM", onSignal);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
// Default probe: try to require node:sqlite. Any failure (ERR_UNKNOWN_BUILTIN_MODULE on early
|
|
143
|
+
// 22.x, or a thrown ExperimentalWarning that escaped) means we need the flag. The require is
|
|
144
|
+
// guarded inside a try; we never throw past here.
|
|
145
|
+
function defaultSqliteProbe() {
|
|
146
|
+
try {
|
|
147
|
+
const req = createRequire(import.meta.url);
|
|
148
|
+
req("node:sqlite");
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Returns true if `--experimental-sqlite` is already on execArgv, meaning we are inside the child
|
|
156
|
+
// already and must not loop. Inspects both the supplied execArgv source (Node-level flags the
|
|
157
|
+
// parent inherited) AND NODE_OPTIONS (where the flag may live in env).
|
|
158
|
+
function alreadyFlagged(env, execArgv) {
|
|
159
|
+
if (execArgv.includes(SQLITE_FLAG))
|
|
160
|
+
return true;
|
|
161
|
+
const nodeOptions = env.NODE_OPTIONS;
|
|
162
|
+
return typeof nodeOptions === "string" && nodeOptions.includes(SQLITE_FLAG);
|
|
163
|
+
}
|
|
164
|
+
// Re-spawns the current process with --experimental-sqlite prepended, inheriting stdio. Returns
|
|
165
|
+
// the exit code the child terminated with so the parent can propagate it.
|
|
166
|
+
export async function reExecWithSqliteFlag(_env, spawnFn) {
|
|
167
|
+
const entry = process.argv[1];
|
|
168
|
+
if (entry === undefined)
|
|
169
|
+
return 1;
|
|
170
|
+
const childArgs = [SQLITE_FLAG, ...process.execArgv, entry, ...process.argv.slice(2)];
|
|
171
|
+
const child = spawnFn(process.execPath, childArgs, { stdio: "inherit" });
|
|
172
|
+
const forwardSigint = () => {
|
|
173
|
+
child.kill("SIGINT");
|
|
174
|
+
};
|
|
175
|
+
const forwardSigterm = () => {
|
|
176
|
+
child.kill("SIGTERM");
|
|
177
|
+
};
|
|
178
|
+
process.on("SIGINT", forwardSigint);
|
|
179
|
+
process.on("SIGTERM", forwardSigterm);
|
|
180
|
+
return new Promise((res) => {
|
|
181
|
+
child.on("exit", (code, signal) => {
|
|
182
|
+
process.removeListener("SIGINT", forwardSigint);
|
|
183
|
+
process.removeListener("SIGTERM", forwardSigterm);
|
|
184
|
+
if (typeof code === "number") {
|
|
185
|
+
res(code);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
res(signal === null ? 1 : 128);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Returns undefined when no re-exec is needed; returns the child's exit code when a re-exec
|
|
193
|
+
// happened. The guard never runs when an injected test factory bypasses the production loop, and
|
|
194
|
+
// it never re-loops once --experimental-sqlite is already in execArgv.
|
|
195
|
+
async function maybeReExecForSqlite(env, deps) {
|
|
196
|
+
if (deps.createServer !== undefined)
|
|
197
|
+
return undefined;
|
|
198
|
+
const execArgv = (deps.currentExecArgv ?? (() => process.execArgv))();
|
|
199
|
+
if (alreadyFlagged(env, execArgv))
|
|
200
|
+
return undefined;
|
|
201
|
+
const probe = deps.sqliteProbe ?? defaultSqliteProbe;
|
|
202
|
+
if (probe())
|
|
203
|
+
return undefined;
|
|
204
|
+
const spawnFn = deps.spawnFn ?? spawn;
|
|
205
|
+
return reExecWithSqliteFlag(env, spawnFn);
|
|
206
|
+
}
|
|
207
|
+
export async function runUiCli(args, io, env, deps = {}) {
|
|
208
|
+
const effectiveEnv = loadLocalKeikoEnv(deps.cwd ?? process.cwd(), env);
|
|
209
|
+
const reExec = await maybeReExecForSqlite(effectiveEnv, deps);
|
|
210
|
+
if (reExec !== undefined)
|
|
211
|
+
return reExec;
|
|
212
|
+
const parsed = parseUiArgs(args);
|
|
213
|
+
if (parsed === null) {
|
|
214
|
+
io.err(USAGE);
|
|
215
|
+
return 2;
|
|
216
|
+
}
|
|
217
|
+
const staticRoot = deps.staticRoot ?? defaultStaticRoot();
|
|
218
|
+
if (!existsSync(staticRoot)) {
|
|
219
|
+
io.err(`keiko ui: UI assets not found at ${staticRoot}. Run \`npm run build:ui\` first.\n`);
|
|
220
|
+
return 1;
|
|
221
|
+
}
|
|
222
|
+
const csp = await loadCspHeader(deps.hashesFile ?? join(staticRoot, "..", "csp-hashes.json"));
|
|
223
|
+
const handlerDeps = buildUiHandlerDeps({
|
|
224
|
+
configPath: resolveUiConfigPath(parsed, effectiveEnv),
|
|
225
|
+
evidenceDir: parsed.evidenceDir,
|
|
226
|
+
uiDbPath: parsed.uiDbPath,
|
|
227
|
+
env: effectiveEnv,
|
|
228
|
+
});
|
|
229
|
+
const factory = deps.createServer ?? createUiServer;
|
|
230
|
+
const server = await factory({ staticRoot, csp, port: parsed.port, handlerDeps });
|
|
231
|
+
applyServerTimeouts(server);
|
|
232
|
+
await listen(server, parsed.port);
|
|
233
|
+
io.out(`Keiko UI listening on http://${UI_HOST}:${String(parsed.port)}\n`);
|
|
234
|
+
// Block only in the real CLI path (no injected factory). Injected-server tests skip blocking so
|
|
235
|
+
// they don't hang; the real process must stay alive until signalled.
|
|
236
|
+
if (deps.createServer === undefined) {
|
|
237
|
+
await waitForShutdown(server);
|
|
238
|
+
}
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// `keiko verify` — the real verification gate (NOT a dry-run). It detects the workspace, builds a
|
|
2
|
+
// plan from the detected npm scripts, runs the allowlisted commands through the #6 safe tool layer
|
|
3
|
+
// under per-command resource limits, and prints a redacted human table (or --json of the report).
|
|
4
|
+
// Exit 0 when overall status is `passed`; 1 when any step did not pass or a workspace/runtime error
|
|
5
|
+
// occurred; 2 on a usage error. Mirrors runContextCli's structure (flag parsing, --json, typed
|
|
6
|
+
// error catch at the boundary).
|
|
7
|
+
import { detectWorkspace } from "../workspace/index.js";
|
|
8
|
+
import { WorkspaceError } from "../workspace/index.js";
|
|
9
|
+
import { buildVerificationPlan, buildVerificationSummary, detectScripts, EmptyPlanError, runVerification, VerificationError, } from "../verification/index.js";
|
|
10
|
+
const VALID_KINDS = new Set([
|
|
11
|
+
"test",
|
|
12
|
+
"targeted-test",
|
|
13
|
+
"typecheck",
|
|
14
|
+
"lint",
|
|
15
|
+
"build",
|
|
16
|
+
]);
|
|
17
|
+
const USAGE = `Usage:
|
|
18
|
+
keiko verify [--dir PATH] [--only KIND[,KIND]] [--changed FILE[,FILE]] [--json]
|
|
19
|
+
|
|
20
|
+
Runs the project's own gates (typecheck, lint, test, build) through the safe tool
|
|
21
|
+
layer under per-command resource limits and prints a redacted evidence summary.
|
|
22
|
+
KIND is one of: test, targeted-test, typecheck, lint, build.
|
|
23
|
+
`;
|
|
24
|
+
// Returns the value of a `--flag value` pair, undefined if absent, or null if present without a
|
|
25
|
+
// value (a usage error) — identical contract to runContextCli's flagValue.
|
|
26
|
+
function flagValue(args, name) {
|
|
27
|
+
const i = args.indexOf(name);
|
|
28
|
+
if (i === -1) {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
const value = args[i + 1];
|
|
32
|
+
return value === undefined || value.startsWith("--") ? null : value;
|
|
33
|
+
}
|
|
34
|
+
function parseKinds(raw) {
|
|
35
|
+
const parts = raw.split(",").map((p) => p.trim());
|
|
36
|
+
if (parts.some((p) => !VALID_KINDS.has(p))) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
return parts;
|
|
40
|
+
}
|
|
41
|
+
function parseArgs(args) {
|
|
42
|
+
const dirRaw = flagValue(args, "--dir");
|
|
43
|
+
const onlyRaw = flagValue(args, "--only");
|
|
44
|
+
const changedRaw = flagValue(args, "--changed");
|
|
45
|
+
if (dirRaw === null || onlyRaw === null || changedRaw === null) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const only = onlyRaw === undefined ? undefined : parseKinds(onlyRaw);
|
|
49
|
+
if (only === null) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const changed = changedRaw === undefined ? undefined : changedRaw.split(",").map((p) => p.trim());
|
|
53
|
+
return { dir: dirRaw ?? ".", only, changed, json: args.includes("--json") };
|
|
54
|
+
}
|
|
55
|
+
async function runPlan(parsed) {
|
|
56
|
+
const workspace = detectWorkspace(parsed.dir);
|
|
57
|
+
const catalog = detectScripts(workspace);
|
|
58
|
+
const plan = buildVerificationPlan(workspace, catalog, {
|
|
59
|
+
only: parsed.only,
|
|
60
|
+
changedFiles: parsed.changed,
|
|
61
|
+
});
|
|
62
|
+
if (plan.steps.length === 0) {
|
|
63
|
+
throw new EmptyPlanError("verification plan contains no runnable or skipped steps");
|
|
64
|
+
}
|
|
65
|
+
return runVerification(plan, { workspace });
|
|
66
|
+
}
|
|
67
|
+
function renderText(report, io) {
|
|
68
|
+
const summary = buildVerificationSummary(report);
|
|
69
|
+
io.out(`Verification: ${summary.overallStatus} (${String(summary.durationMs)}ms)\n`);
|
|
70
|
+
io.out("KIND\tSTATUS\tEXIT\tMS\tCOMMAND\tDETAIL\n");
|
|
71
|
+
for (const r of summary.results) {
|
|
72
|
+
const exit = r.exitCode === null ? "-" : String(r.exitCode);
|
|
73
|
+
io.out(`${r.kind}\t${r.status}\t${exit}\t${String(r.durationMs)}\t${r.command}\t${r.detail ?? ""}\n`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
export async function runVerifyCli(args, io) {
|
|
77
|
+
const parsed = parseArgs(args);
|
|
78
|
+
if (parsed === null) {
|
|
79
|
+
io.err(USAGE);
|
|
80
|
+
return 2;
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const report = await runPlan(parsed);
|
|
84
|
+
if (parsed.json) {
|
|
85
|
+
io.out(`${JSON.stringify(buildVerificationSummary(report), null, 2)}\n`);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
renderText(report, io);
|
|
89
|
+
}
|
|
90
|
+
return report.overallStatus === "passed" ? 0 : 1;
|
|
91
|
+
}
|
|
92
|
+
catch (error) {
|
|
93
|
+
if (error instanceof WorkspaceError) {
|
|
94
|
+
io.err(`Error [${error.code}]: ${error.message}\n`);
|
|
95
|
+
return 1;
|
|
96
|
+
}
|
|
97
|
+
if (error instanceof VerificationError) {
|
|
98
|
+
io.err(`Error [${error.code}]: ${error.message}\n`);
|
|
99
|
+
return 1;
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
}
|