@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
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// All tool-layer interfaces and the frozen default tables (env allowlist, command rules,
|
|
2
|
+
// sandbox policy, limits, host config). No runtime logic lives here beyond the frozen
|
|
3
|
+
// constant tables the type layer exposes as values, mirroring the ADR-0003/0004/0005
|
|
4
|
+
// `types.ts` precedent. `readonly` everywhere; optional props are `| undefined` because
|
|
5
|
+
// exactOptionalPropertyTypes is on. Imports end `.js`, double quotes, `type` keyword.
|
|
6
|
+
// Cross-platform name allowlist. Only names that are PRESENT in the parent are copied, so an
|
|
7
|
+
// absent Windows var on POSIX (or vice versa) is simply skipped.
|
|
8
|
+
//
|
|
9
|
+
// HOME and USERPROFILE are deliberately ABSENT (C5). Forwarding the developer's real home would let
|
|
10
|
+
// a subprocess read ~/.npmrc (npm tokens), ~/.git-credentials, and ~/.aws/… by standard home-dir
|
|
11
|
+
// lookup. runCommand instead injects an ephemeral, EMPTY per-run dir as HOME/USERPROFILE so those
|
|
12
|
+
// lookups resolve to nothing (ADR-0006 D2 Dimension 1).
|
|
13
|
+
export const DEFAULT_ENV_ALLOWLIST = Object.freeze([
|
|
14
|
+
"PATH",
|
|
15
|
+
"LANG",
|
|
16
|
+
"LC_ALL",
|
|
17
|
+
"LC_CTYPE",
|
|
18
|
+
"TZ",
|
|
19
|
+
"TERM",
|
|
20
|
+
"TMPDIR",
|
|
21
|
+
// Windows essentials so spawned tools resolve the shell-less executable correctly.
|
|
22
|
+
"SystemRoot",
|
|
23
|
+
"SystemDrive",
|
|
24
|
+
"PATHEXT",
|
|
25
|
+
"COMSPEC",
|
|
26
|
+
"NUMBER_OF_PROCESSORS",
|
|
27
|
+
"WINDIR",
|
|
28
|
+
]);
|
|
29
|
+
export const DEFAULT_SANDBOX_POLICY = {
|
|
30
|
+
envAllowlist: DEFAULT_ENV_ALLOWLIST,
|
|
31
|
+
network: "inherit",
|
|
32
|
+
maxOutputBytes: 262_144,
|
|
33
|
+
defaultTimeoutMs: 30_000,
|
|
34
|
+
terminationGraceMs: 2_000,
|
|
35
|
+
};
|
|
36
|
+
// Minimal, justified default rules. Everything not listed is denied (deny-by-default).
|
|
37
|
+
export const DEFAULT_COMMAND_RULES = Object.freeze([
|
|
38
|
+
{
|
|
39
|
+
executable: "npm",
|
|
40
|
+
// Read-only npm only. Mutating/package-installing subcommands are excluded by omission.
|
|
41
|
+
allowedSubcommands: Object.freeze([
|
|
42
|
+
"audit",
|
|
43
|
+
"ls",
|
|
44
|
+
"list",
|
|
45
|
+
"outdated",
|
|
46
|
+
"view",
|
|
47
|
+
"info",
|
|
48
|
+
"help",
|
|
49
|
+
"ping",
|
|
50
|
+
]),
|
|
51
|
+
// `-c`/`--call` execute a command string in a shell; deny outright (S-H2).
|
|
52
|
+
denyFlags: Object.freeze(["-c", "--call"]),
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
executable: "git",
|
|
56
|
+
// READ-ONLY git only; push/reset/checkout/commit/merge/rebase/clean/config/remote denied.
|
|
57
|
+
allowedSubcommands: Object.freeze([
|
|
58
|
+
"status",
|
|
59
|
+
"diff",
|
|
60
|
+
"log",
|
|
61
|
+
"show",
|
|
62
|
+
"rev-parse",
|
|
63
|
+
"ls-files",
|
|
64
|
+
"describe",
|
|
65
|
+
"blame",
|
|
66
|
+
"cat-file",
|
|
67
|
+
]),
|
|
68
|
+
// Global value flags that precede the subcommand (`git -C DIR <sub>`). Skipping them prevents
|
|
69
|
+
// the value (DIR) from being read as the subcommand (S-H2).
|
|
70
|
+
valueFlags: Object.freeze([
|
|
71
|
+
"-C",
|
|
72
|
+
"-c",
|
|
73
|
+
"--git-dir",
|
|
74
|
+
"--work-tree",
|
|
75
|
+
"--namespace",
|
|
76
|
+
"--exec-path",
|
|
77
|
+
]),
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
export const DEFAULT_PATCH_LIMITS = {
|
|
81
|
+
maxPatchBytes: 65_536,
|
|
82
|
+
maxChangedLines: 2_000,
|
|
83
|
+
maxFilesChanged: 50,
|
|
84
|
+
};
|
|
85
|
+
export const DEFAULT_TOOL_HOST_CONFIG = {
|
|
86
|
+
sandbox: DEFAULT_SANDBOX_POLICY,
|
|
87
|
+
commandRules: DEFAULT_COMMAND_RULES,
|
|
88
|
+
patchLimits: DEFAULT_PATCH_LIMITS,
|
|
89
|
+
applyEnabled: false,
|
|
90
|
+
maxReadBytes: 262_144,
|
|
91
|
+
};
|
|
92
|
+
// Deep-merges a caller override over the defaults: the nested sandbox/patchLimits objects merge
|
|
93
|
+
// field-by-field so a partial override never drops an unspecified default (S-M2).
|
|
94
|
+
export function resolveToolHostConfig(input) {
|
|
95
|
+
const base = DEFAULT_TOOL_HOST_CONFIG;
|
|
96
|
+
return {
|
|
97
|
+
sandbox: { ...base.sandbox, ...input?.sandbox },
|
|
98
|
+
commandRules: input?.commandRules ?? base.commandRules,
|
|
99
|
+
patchLimits: { ...base.patchLimits, ...input?.patchLimits },
|
|
100
|
+
applyEnabled: input?.applyEnabled ?? base.applyEnabled,
|
|
101
|
+
maxReadBytes: input?.maxReadBytes ?? base.maxReadBytes,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export interface WorkspaceWriter {
|
|
2
|
+
readonly writeFileUtf8: (absolutePath: string, content: string) => void;
|
|
3
|
+
readonly mkdirp: (absoluteDir: string) => void;
|
|
4
|
+
readonly remove: (absolutePath: string) => void;
|
|
5
|
+
readonly rename: (fromAbsolute: string, toAbsolute: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare const nodeWorkspaceWriter: WorkspaceWriter;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// The single controlled filesystem-WRITE boundary (ADR-0006 D2). WorkspaceFs stays read-only
|
|
2
|
+
// (ADR-0005); all mutation goes through this port so the apply phase is auditable and testable
|
|
3
|
+
// with an in-memory fake. Callers (patch.ts) MUST pre-validate every absolute path via
|
|
4
|
+
// resolveWithinWorkspace + isDenied before handing it here; this adapter does no validation
|
|
5
|
+
// itself — it is the effectful edge, kept deliberately thin. Synchronous, mirroring nodeWorkspaceFs.
|
|
6
|
+
import { mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
7
|
+
export const nodeWorkspaceWriter = {
|
|
8
|
+
writeFileUtf8: (absolutePath, content) => {
|
|
9
|
+
writeFileSync(absolutePath, content, "utf8");
|
|
10
|
+
},
|
|
11
|
+
mkdirp: (absoluteDir) => {
|
|
12
|
+
mkdirSync(absoluteDir, { recursive: true });
|
|
13
|
+
},
|
|
14
|
+
remove: (absolutePath) => {
|
|
15
|
+
rmSync(absolutePath, { force: true });
|
|
16
|
+
},
|
|
17
|
+
rename: (fromAbsolute, toAbsolute) => {
|
|
18
|
+
renameSync(fromAbsolute, toAbsolute);
|
|
19
|
+
},
|
|
20
|
+
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { UiHandlerDeps } from "./deps.js";
|
|
2
|
+
import { type HandlerOutcome, type RouteContext, type RouteResult } from "./routes.js";
|
|
3
|
+
export declare function handleBrowserStatus(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
4
|
+
export declare function handleCreateBrowserSession(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
5
|
+
export declare function handleDeleteBrowserSession(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
6
|
+
export declare function handleBrowserNavigate(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
7
|
+
export declare function handleBrowserScreenshot(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
8
|
+
export declare function handleBrowserApplyScreenshot(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
9
|
+
export declare function handleBrowserContent(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
10
|
+
export declare function handleBrowserEvents(ctx: RouteContext, deps: UiHandlerDeps): HandlerOutcome;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
// ADR-0017 D8 — eight /api/browser/* BFF route handlers. CSRF guarding is enforced by the
|
|
2
|
+
// server.ts state-changing-request gate (POST/DELETE all flow through it). GET status + GET
|
|
3
|
+
// events are exempt by the same gate. SSE framing reuses the existing sse.ts helpers.
|
|
4
|
+
import { BrowserToolError, } from "../tools/browser/index.js";
|
|
5
|
+
import { SSE_HEADERS, readyMessage } from "./sse.js";
|
|
6
|
+
import { errorBody, STREAMING, } from "./routes.js";
|
|
7
|
+
const MAX_BROWSER_BODY_BYTES = 64_000;
|
|
8
|
+
class BodyTooLargeError extends Error {
|
|
9
|
+
constructor() {
|
|
10
|
+
super("browser request body too large");
|
|
11
|
+
this.name = "BodyTooLargeError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function noBrowserDeps() {
|
|
15
|
+
return {
|
|
16
|
+
status: 503,
|
|
17
|
+
body: errorBody("BROWSER_UNAVAILABLE", "Browser tool is not configured for this BFF."),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function requireBrowser(deps) {
|
|
21
|
+
return deps.browser ?? noBrowserDeps();
|
|
22
|
+
}
|
|
23
|
+
function isRouteResult(value) {
|
|
24
|
+
return typeof value.status === "number";
|
|
25
|
+
}
|
|
26
|
+
function toRouteResult(error) {
|
|
27
|
+
return { status: error.status, body: errorBody(error.code, error.message) };
|
|
28
|
+
}
|
|
29
|
+
function readBody(req) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const chunks = [];
|
|
32
|
+
let total = 0;
|
|
33
|
+
let capped = false;
|
|
34
|
+
req.on("data", (chunk) => {
|
|
35
|
+
total += chunk.length;
|
|
36
|
+
if (total > MAX_BROWSER_BODY_BYTES) {
|
|
37
|
+
if (!capped) {
|
|
38
|
+
capped = true;
|
|
39
|
+
chunks.length = 0;
|
|
40
|
+
reject(new BodyTooLargeError());
|
|
41
|
+
req.resume();
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
chunks.push(chunk);
|
|
46
|
+
});
|
|
47
|
+
req.on("end", () => {
|
|
48
|
+
if (!capped)
|
|
49
|
+
resolve(Buffer.concat(chunks).toString("utf8"));
|
|
50
|
+
});
|
|
51
|
+
req.on("error", reject);
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function readJsonObject(req) {
|
|
55
|
+
const raw = await readBody(req);
|
|
56
|
+
if (raw.length === 0)
|
|
57
|
+
return {};
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(raw);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
throw new BrowserToolError("BAD_REQUEST", "Request body is not valid JSON.");
|
|
64
|
+
}
|
|
65
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
66
|
+
throw new BrowserToolError("BAD_REQUEST", "Request body must be a JSON object.");
|
|
67
|
+
}
|
|
68
|
+
return parsed;
|
|
69
|
+
}
|
|
70
|
+
function requireString(body, key) {
|
|
71
|
+
const value = body[key];
|
|
72
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
73
|
+
throw new BrowserToolError("BAD_REQUEST", `Field "${key}" must be a non-empty string.`);
|
|
74
|
+
}
|
|
75
|
+
return value;
|
|
76
|
+
}
|
|
77
|
+
function requireNumber(body, key) {
|
|
78
|
+
const value = body[key];
|
|
79
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
80
|
+
throw new BrowserToolError("BAD_REQUEST", `Field "${key}" must be a finite number.`);
|
|
81
|
+
}
|
|
82
|
+
return value;
|
|
83
|
+
}
|
|
84
|
+
async function runHandler(work) {
|
|
85
|
+
try {
|
|
86
|
+
return await work();
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
if (error instanceof BodyTooLargeError) {
|
|
90
|
+
return {
|
|
91
|
+
status: 413,
|
|
92
|
+
body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (error instanceof BrowserToolError)
|
|
96
|
+
return toRouteResult(error);
|
|
97
|
+
throw error;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export async function handleBrowserStatus(ctx, deps) {
|
|
101
|
+
const guard = requireBrowser(deps);
|
|
102
|
+
if (isRouteResult(guard))
|
|
103
|
+
return guard;
|
|
104
|
+
return runHandler(async () => {
|
|
105
|
+
const portParam = ctx.url.searchParams.get("port");
|
|
106
|
+
if (portParam === null) {
|
|
107
|
+
throw new BrowserToolError("BAD_REQUEST", "Query parameter 'port' is required.");
|
|
108
|
+
}
|
|
109
|
+
const port = Number.parseInt(portParam, 10);
|
|
110
|
+
if (!Number.isFinite(port)) {
|
|
111
|
+
throw new BrowserToolError("BAD_REQUEST", "Query parameter 'port' must be an integer.");
|
|
112
|
+
}
|
|
113
|
+
const status = await guard.checkStatus(port);
|
|
114
|
+
return { status: 200, body: status };
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export async function handleCreateBrowserSession(ctx, deps) {
|
|
118
|
+
const guard = requireBrowser(deps);
|
|
119
|
+
if (isRouteResult(guard))
|
|
120
|
+
return guard;
|
|
121
|
+
return runHandler(async () => {
|
|
122
|
+
const body = await readJsonObject(ctx.req);
|
|
123
|
+
const port = requireNumber(body, "port");
|
|
124
|
+
const meta = await guard.openSession(port);
|
|
125
|
+
return { status: 201, body: meta };
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
export async function handleDeleteBrowserSession(ctx, deps) {
|
|
129
|
+
const guard = requireBrowser(deps);
|
|
130
|
+
if (isRouteResult(guard))
|
|
131
|
+
return guard;
|
|
132
|
+
return runHandler(async () => {
|
|
133
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
134
|
+
await guard.closeSession(sessionId);
|
|
135
|
+
return { status: 200, body: { ok: true } };
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
export async function handleBrowserNavigate(ctx, deps) {
|
|
139
|
+
const guard = requireBrowser(deps);
|
|
140
|
+
if (isRouteResult(guard))
|
|
141
|
+
return guard;
|
|
142
|
+
return runHandler(async () => {
|
|
143
|
+
const body = await readJsonObject(ctx.req);
|
|
144
|
+
const url = requireString(body, "url");
|
|
145
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
146
|
+
const result = await guard.navigate(sessionId, url);
|
|
147
|
+
return { status: 200, body: result };
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
export async function handleBrowserScreenshot(ctx, deps) {
|
|
151
|
+
const guard = requireBrowser(deps);
|
|
152
|
+
if (isRouteResult(guard))
|
|
153
|
+
return guard;
|
|
154
|
+
return runHandler(async () => {
|
|
155
|
+
// Drain the body through readJsonObject so the 64 KB cap (MAX_BROWSER_BODY_BYTES) is
|
|
156
|
+
// enforced even though this handler expects no fields. Raw node:http has no global cap.
|
|
157
|
+
await readJsonObject(ctx.req);
|
|
158
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
159
|
+
const result = await guard.screenshot(sessionId);
|
|
160
|
+
return { status: 200, body: result };
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
export async function handleBrowserApplyScreenshot(ctx, deps) {
|
|
164
|
+
const guard = requireBrowser(deps);
|
|
165
|
+
if (isRouteResult(guard))
|
|
166
|
+
return guard;
|
|
167
|
+
return runHandler(async () => {
|
|
168
|
+
const body = await readJsonObject(ctx.req);
|
|
169
|
+
const captureSeq = requireNumber(body, "captureSeq");
|
|
170
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
171
|
+
const result = await guard.applyScreenshot(sessionId, captureSeq);
|
|
172
|
+
return { status: 200, body: result };
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
export async function handleBrowserContent(ctx, deps) {
|
|
176
|
+
const guard = requireBrowser(deps);
|
|
177
|
+
if (isRouteResult(guard))
|
|
178
|
+
return guard;
|
|
179
|
+
return runHandler(async () => {
|
|
180
|
+
// Drain the body through readJsonObject so the 64 KB cap (MAX_BROWSER_BODY_BYTES) is
|
|
181
|
+
// enforced even though this handler expects no fields. Raw node:http has no global cap.
|
|
182
|
+
await readJsonObject(ctx.req);
|
|
183
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
184
|
+
const result = await guard.content(sessionId);
|
|
185
|
+
return { status: 200, body: result };
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
// SSE — re-uses the existing framer shape. Each browser event becomes one SSE message with
|
|
189
|
+
// event: <kind> and data: <JSON>. A 'ready' synthetic is sent first so clients can transition
|
|
190
|
+
// from connecting to live.
|
|
191
|
+
export function handleBrowserEvents(ctx, deps) {
|
|
192
|
+
const guard = requireBrowser(deps);
|
|
193
|
+
if (isRouteResult(guard))
|
|
194
|
+
return guard;
|
|
195
|
+
const sessionId = ctx.params.sessionId ?? "";
|
|
196
|
+
if (sessionId.length === 0) {
|
|
197
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "sessionId is required.") };
|
|
198
|
+
}
|
|
199
|
+
if (!guard.hasSession(sessionId)) {
|
|
200
|
+
return { status: 404, body: errorBody("SESSION_NOT_FOUND", "Browser session not found.") };
|
|
201
|
+
}
|
|
202
|
+
openBrowserSseStream(ctx.res, guard, sessionId, deps.redactor);
|
|
203
|
+
ctx.req.on("close", () => {
|
|
204
|
+
ctx.res.end();
|
|
205
|
+
});
|
|
206
|
+
return STREAMING;
|
|
207
|
+
}
|
|
208
|
+
function openBrowserSseStream(res, manager, sessionId, redactor) {
|
|
209
|
+
res.writeHead(200, SSE_HEADERS);
|
|
210
|
+
let seq = 0;
|
|
211
|
+
const unsubscribe = manager.subscribe(sessionId, (event) => {
|
|
212
|
+
seq += 1;
|
|
213
|
+
writeBrowserEvent(res, event, seq, redactor);
|
|
214
|
+
if (event.kind === "session-closed") {
|
|
215
|
+
unsubscribe();
|
|
216
|
+
res.end();
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
res.write(readyMessage());
|
|
220
|
+
res.on("close", () => {
|
|
221
|
+
unsubscribe();
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
function writeBrowserEvent(res, event, seq, redactor) {
|
|
225
|
+
const redacted = redactor(event);
|
|
226
|
+
const data = JSON.stringify(redacted);
|
|
227
|
+
const frame = `id: ${String(seq)}\nevent: browser:${event.kind}\ndata: ${data}\n\n`;
|
|
228
|
+
if (!res.write(frame)) {
|
|
229
|
+
res.destroy();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { UiHandlerDeps } from "./deps.js";
|
|
2
|
+
import type { RouteContext, RouteResult } from "./routes.js";
|
|
3
|
+
export declare function handleCreateDesktopChat(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
4
|
+
export declare function handleSendDesktopChat(ctx: RouteContext, deps: UiHandlerDeps): Promise<RouteResult>;
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
// Desktop chat BFF routes for the Keiko canvas UI. These routes intentionally keep the model call
|
|
2
|
+
// behind the existing ModelPort/Gateway boundary: the browser sends only chat content and a registry
|
|
3
|
+
// model id, while provider endpoints and keys remain resolved from the local gateway config/.env.
|
|
4
|
+
import { basename } from "node:path";
|
|
5
|
+
import { GatewayError, findCapability } from "../gateway/index.js";
|
|
6
|
+
import { UiStoreError, isProjectAvailable, } from "./store/index.js";
|
|
7
|
+
import { validateProjectPath } from "./store/validation.js";
|
|
8
|
+
import { errorBody } from "./routes.js";
|
|
9
|
+
const DEFAULT_CHAT_MODEL = "gpt-oss-120b";
|
|
10
|
+
const DEFAULT_CHAT_TITLE = "GPT OSS 120B";
|
|
11
|
+
const MAX_BODY_BYTES = 128_000;
|
|
12
|
+
const MAX_CHAT_INPUT_CHARS = 16_000;
|
|
13
|
+
const MAX_CONTEXT_MESSAGES = 24;
|
|
14
|
+
class BodyTooLargeError extends Error {
|
|
15
|
+
constructor() {
|
|
16
|
+
super("request body too large");
|
|
17
|
+
this.name = "BodyTooLargeError";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function isRecord(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
|
+
}
|
|
23
|
+
function readBody(req) {
|
|
24
|
+
return new Promise((resolveBody, reject) => {
|
|
25
|
+
const chunks = [];
|
|
26
|
+
let total = 0;
|
|
27
|
+
let capped = false;
|
|
28
|
+
req.on("data", (chunk) => {
|
|
29
|
+
total += chunk.length;
|
|
30
|
+
if (total > MAX_BODY_BYTES) {
|
|
31
|
+
if (!capped) {
|
|
32
|
+
capped = true;
|
|
33
|
+
chunks.length = 0;
|
|
34
|
+
reject(new BodyTooLargeError());
|
|
35
|
+
req.resume();
|
|
36
|
+
}
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
chunks.push(chunk);
|
|
40
|
+
});
|
|
41
|
+
req.on("end", () => {
|
|
42
|
+
if (!capped) {
|
|
43
|
+
resolveBody(Buffer.concat(chunks).toString("utf8"));
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
req.on("error", reject);
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
async function readJsonObject(req) {
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = await readBody(req);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof BodyTooLargeError) {
|
|
56
|
+
return {
|
|
57
|
+
status: 413,
|
|
58
|
+
body: errorBody("PAYLOAD_TOO_LARGE", "Request body exceeds the size limit."),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
let parsed;
|
|
64
|
+
try {
|
|
65
|
+
parsed = raw.length === 0 ? {} : JSON.parse(raw);
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "Request body is not valid JSON.") };
|
|
69
|
+
}
|
|
70
|
+
if (!isRecord(parsed)) {
|
|
71
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "Request body must be a JSON object.") };
|
|
72
|
+
}
|
|
73
|
+
return parsed;
|
|
74
|
+
}
|
|
75
|
+
function isRouteResult(value) {
|
|
76
|
+
return isRecord(value) && typeof value.status === "number" && "body" in value;
|
|
77
|
+
}
|
|
78
|
+
function modelFromBody(body) {
|
|
79
|
+
const modelId = typeof body.modelId === "string" && body.modelId.length > 0
|
|
80
|
+
? body.modelId
|
|
81
|
+
: DEFAULT_CHAT_MODEL;
|
|
82
|
+
const capability = findCapability(modelId);
|
|
83
|
+
if (capability?.kind !== "chat") {
|
|
84
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "modelId must be a chat model registry id.") };
|
|
85
|
+
}
|
|
86
|
+
return modelId;
|
|
87
|
+
}
|
|
88
|
+
function pickProjectPath(body, deps) {
|
|
89
|
+
const supplied = typeof body.projectPath === "string" && body.projectPath.length > 0
|
|
90
|
+
? body.projectPath
|
|
91
|
+
: undefined;
|
|
92
|
+
if (supplied !== undefined) {
|
|
93
|
+
return validateProjectPath(supplied, { mustExist: true });
|
|
94
|
+
}
|
|
95
|
+
const available = deps.store
|
|
96
|
+
.listProjects()
|
|
97
|
+
.find((project) => isProjectAvailable(project));
|
|
98
|
+
if (available !== undefined) {
|
|
99
|
+
return available.path;
|
|
100
|
+
}
|
|
101
|
+
return validateProjectPath(process.cwd(), { mustExist: true });
|
|
102
|
+
}
|
|
103
|
+
function ensureProject(deps, path) {
|
|
104
|
+
const existing = deps.store.listProjects().find((project) => project.path === path);
|
|
105
|
+
if (existing !== undefined) {
|
|
106
|
+
deps.store.updateProject(path, {});
|
|
107
|
+
return existing;
|
|
108
|
+
}
|
|
109
|
+
const name = basename(path) || "Local workspace";
|
|
110
|
+
return deps.store.createProject(path, name);
|
|
111
|
+
}
|
|
112
|
+
function findChat(deps, projectPath, chatId) {
|
|
113
|
+
return deps.store.listChats(projectPath).find((chat) => chat.id === chatId);
|
|
114
|
+
}
|
|
115
|
+
function chatEnvelope(deps, project, chat) {
|
|
116
|
+
const projects = deps.store.listProjects().map((item) => ({
|
|
117
|
+
...item,
|
|
118
|
+
available: isProjectAvailable(item),
|
|
119
|
+
}));
|
|
120
|
+
const chats = deps.store.listChats(project.path);
|
|
121
|
+
const messages = deps.store.listMessages(chat.id);
|
|
122
|
+
return {
|
|
123
|
+
project: { ...project, available: isProjectAvailable(project) },
|
|
124
|
+
chat,
|
|
125
|
+
messages,
|
|
126
|
+
projects,
|
|
127
|
+
chats,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function gatewayErrorResult(error) {
|
|
131
|
+
const status = error.code === "GATEWAY_AUTHENTICATION" ? 401 : error.retryable ? 503 : 502;
|
|
132
|
+
return { status, body: errorBody(error.code, error.message) };
|
|
133
|
+
}
|
|
134
|
+
function desktopChatErrorResult(error) {
|
|
135
|
+
if (error instanceof GatewayError) {
|
|
136
|
+
return gatewayErrorResult(error);
|
|
137
|
+
}
|
|
138
|
+
if (error instanceof UiStoreError) {
|
|
139
|
+
return { status: error.status, body: errorBody(error.code, error.message) };
|
|
140
|
+
}
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
function messageForGateway(message) {
|
|
144
|
+
if (message.role !== "user" && message.role !== "assistant") {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
return { role: message.role, content: message.content };
|
|
148
|
+
}
|
|
149
|
+
function conversationForGateway(messages) {
|
|
150
|
+
const usable = messages
|
|
151
|
+
.map(messageForGateway)
|
|
152
|
+
.filter((message) => message !== null)
|
|
153
|
+
.slice(-MAX_CONTEXT_MESSAGES);
|
|
154
|
+
return [
|
|
155
|
+
{
|
|
156
|
+
role: "system",
|
|
157
|
+
content: "You are Keiko, an enterprise developer-assist AI. Be concise, practical, and explicit about uncertainty. Do not claim tool access you do not have in this chat.",
|
|
158
|
+
},
|
|
159
|
+
...usable,
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
function sendRequestFromBody(body) {
|
|
163
|
+
const chatId = typeof body.chatId === "string" ? body.chatId : "";
|
|
164
|
+
const projectPath = typeof body.projectPath === "string" ? body.projectPath : "";
|
|
165
|
+
const content = typeof body.content === "string" ? body.content.trim() : "";
|
|
166
|
+
if (chatId.length === 0 || projectPath.length === 0) {
|
|
167
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "chatId and projectPath are required.") };
|
|
168
|
+
}
|
|
169
|
+
if (content.length === 0 || content.length > MAX_CHAT_INPUT_CHARS) {
|
|
170
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "content must be between 1 and 16000 characters.") };
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
chatId,
|
|
174
|
+
projectPath,
|
|
175
|
+
content,
|
|
176
|
+
modelId: typeof body.modelId === "string" && body.modelId.length > 0 ? body.modelId : undefined,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function invalidChatModelResult(modelId) {
|
|
180
|
+
const capability = findCapability(modelId);
|
|
181
|
+
if (capability?.kind === "chat") {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
return { status: 400, body: errorBody("BAD_REQUEST", "modelId must be a chat model registry id.") };
|
|
185
|
+
}
|
|
186
|
+
function createUserMessage(deps, request) {
|
|
187
|
+
return deps.store.createMessage({
|
|
188
|
+
chatId: request.chatId,
|
|
189
|
+
role: "user",
|
|
190
|
+
content: request.content,
|
|
191
|
+
timestamp: Date.now(),
|
|
192
|
+
runId: undefined,
|
|
193
|
+
workflowId: undefined,
|
|
194
|
+
workflowStatus: undefined,
|
|
195
|
+
shortResult: undefined,
|
|
196
|
+
taskType: undefined,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
function createAssistantMessage(deps, request, content) {
|
|
200
|
+
return deps.store.createMessage({
|
|
201
|
+
chatId: request.chatId,
|
|
202
|
+
role: "assistant",
|
|
203
|
+
content: content.length > 0 ? content : "The model returned an empty response.",
|
|
204
|
+
timestamp: Date.now(),
|
|
205
|
+
runId: undefined,
|
|
206
|
+
workflowId: undefined,
|
|
207
|
+
workflowStatus: undefined,
|
|
208
|
+
shortResult: undefined,
|
|
209
|
+
taskType: undefined,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
async function persistModelChatTurn(deps, request, chat, modelId) {
|
|
213
|
+
const model = deps.modelPortFactory(modelId);
|
|
214
|
+
if (model === undefined) {
|
|
215
|
+
return { status: 400, body: errorBody("NO_MODEL", "No model provider is configured.") };
|
|
216
|
+
}
|
|
217
|
+
try {
|
|
218
|
+
const userMessage = createUserMessage(deps, request);
|
|
219
|
+
const response = await model.call({
|
|
220
|
+
modelId,
|
|
221
|
+
messages: conversationForGateway(deps.store.listMessages(request.chatId)),
|
|
222
|
+
stream: false,
|
|
223
|
+
}, new AbortController().signal);
|
|
224
|
+
const assistantMessage = createAssistantMessage(deps, request, response.content);
|
|
225
|
+
const chatPatch = chat.title === DEFAULT_CHAT_TITLE
|
|
226
|
+
? { selectedModel: modelId, title: request.content.slice(0, 60) }
|
|
227
|
+
: { selectedModel: modelId };
|
|
228
|
+
return {
|
|
229
|
+
status: 200,
|
|
230
|
+
body: {
|
|
231
|
+
chat: deps.store.updateChat(request.chatId, chatPatch),
|
|
232
|
+
messages: [userMessage, assistantMessage],
|
|
233
|
+
usage: response.usage,
|
|
234
|
+
},
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
return desktopChatErrorResult(error);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
export async function handleCreateDesktopChat(ctx, deps) {
|
|
242
|
+
const body = await readJsonObject(ctx.req);
|
|
243
|
+
if (isRouteResult(body))
|
|
244
|
+
return body;
|
|
245
|
+
const modelId = modelFromBody(body);
|
|
246
|
+
if (isRouteResult(modelId))
|
|
247
|
+
return modelId;
|
|
248
|
+
try {
|
|
249
|
+
const projectPath = pickProjectPath(body, deps);
|
|
250
|
+
const project = ensureProject(deps, projectPath);
|
|
251
|
+
const title = typeof body.title === "string" && body.title.trim().length > 0
|
|
252
|
+
? body.title.trim()
|
|
253
|
+
: DEFAULT_CHAT_TITLE;
|
|
254
|
+
const chat = deps.store.createChat(project.path, title, modelId);
|
|
255
|
+
return { status: 201, body: chatEnvelope(deps, project, chat) };
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
if (error instanceof UiStoreError) {
|
|
259
|
+
return { status: error.status, body: errorBody(error.code, error.message) };
|
|
260
|
+
}
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
export async function handleSendDesktopChat(ctx, deps) {
|
|
265
|
+
const body = await readJsonObject(ctx.req);
|
|
266
|
+
if (isRouteResult(body))
|
|
267
|
+
return body;
|
|
268
|
+
const request = sendRequestFromBody(body);
|
|
269
|
+
if (isRouteResult(request))
|
|
270
|
+
return request;
|
|
271
|
+
const normalizedProjectPath = validateProjectPath(request.projectPath, { mustExist: false });
|
|
272
|
+
const chat = findChat(deps, normalizedProjectPath, request.chatId);
|
|
273
|
+
if (chat === undefined) {
|
|
274
|
+
return { status: 404, body: errorBody("NOT_FOUND", "Chat not found.") };
|
|
275
|
+
}
|
|
276
|
+
const modelId = request.modelId ?? chat.selectedModel;
|
|
277
|
+
const invalidModel = invalidChatModelResult(modelId);
|
|
278
|
+
if (invalidModel !== undefined)
|
|
279
|
+
return invalidModel;
|
|
280
|
+
return persistModelChatTurn(deps, request, chat, modelId);
|
|
281
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
[
|
|
2
|
+
"'sha256-FhLHRUQz4c4ntLU9VkfEesX7PnzNLENSe/16Hi523Kk='",
|
|
3
|
+
"'sha256-NMmsYxPlvKu6BMNDUuiUA/0HWXXhODWSkUJ3CrerHAI='",
|
|
4
|
+
"'sha256-OBTN3RiyCV4Bq7dFqZ5a2pAXjnCcCYeTJMO2I/LYKeo='",
|
|
5
|
+
"'sha256-OogwkdfeAY//FSbFGNeTewmi/U11IUHAkAgKNEwJCG0='",
|
|
6
|
+
"'sha256-U9W+ZoRW19rf6ohEfUh2oSN8UmJ8mZjCoxp31AbEGYM='",
|
|
7
|
+
"'sha256-bDep1P+WNj2FZ2j0g3tUvt5ISNYG1nCzFE65Y9sE1c8='",
|
|
8
|
+
"'sha256-bg+CWjI8RppcgHYH6RuW4z4OnLAUEUPDXRoYUo9Tyok='",
|
|
9
|
+
"'sha256-iqYTUJ5u/EtWNaG9+D7Y0DM2wPtGyVS2aRVXbTOsfag='",
|
|
10
|
+
"'sha256-oJekvGO4J2NYcfsZB+NVPai5HyR1UVOFcmKB3PORB8M='",
|
|
11
|
+
"'sha256-pQjgKfZ7Pcb9s5HrqqpJnRrMazbJ0Nhk6e1aFQNuFsU='",
|
|
12
|
+
"'sha256-q3VO3K+1hbob0r8DheOST7SIt4DQWr76alv4VzyZ44s='",
|
|
13
|
+
"'sha256-qBQ7RdQKJEJuW7Fj1MbGjDbF6lnRdfu+KV0V4A5MTRg='",
|
|
14
|
+
"'sha256-qjuzziE6xLU3Cras89VlShlRYHgYZuOxceXUDmuvClo='",
|
|
15
|
+
"'sha256-xLP5QIbvR88RAxDKoSWqs6CVxNIRu17hhr7S/Q6hlU0='",
|
|
16
|
+
"'sha256-xz80fPjhAczg/tByXnm3xfZrdAUWODPmQtD4solyj1c='"
|
|
17
|
+
]
|
package/dist/ui/csp.d.ts
ADDED