tinybeeai 0.0.0-stage → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.railway/railway.ts +83 -0
- package/.railway/tsconfig.json +13 -0
- package/AGENTS.md +102 -0
- package/Dockerfile +108 -0
- package/LICENSE +21 -0
- package/README.md +275 -2
- package/bin/localdost.js +29 -0
- package/bin/tinybeeai.js +28 -0
- package/compose.yml +63 -0
- package/demo/payloads/ci_failure.json +46 -0
- package/demo/payloads/outage.json +18 -0
- package/demo/payloads/pr_comment_edited.json +50 -0
- package/demo/payloads/pr_merged.json +80 -0
- package/demo/payloads/pr_opened.json +69 -0
- package/demo/payloads/pr_synchronize.json +69 -0
- package/demo/payloads/ticket_created.json +138 -0
- package/demo/sample-app/playwright.config.ts +20 -0
- package/demo/sample-app/server.ts +163 -0
- package/demo/sample-app/tests/checkout.spec.ts +13 -0
- package/demo/sample-app/tests/smoke.spec.ts +8 -0
- package/dist/agent/approval-retries.js +59 -0
- package/dist/agent/approval-retries.js.map +1 -0
- package/dist/agent/approvals.js +390 -0
- package/dist/agent/approvals.js.map +1 -0
- package/dist/agent/chat-bridge.js +154 -0
- package/dist/agent/chat-bridge.js.map +1 -0
- package/dist/agent/chat-lead.js +475 -0
- package/dist/agent/chat-lead.js.map +1 -0
- package/dist/agent/chat-loop.js +106 -0
- package/dist/agent/chat-loop.js.map +1 -0
- package/dist/agent/chat-mcp.js +49 -0
- package/dist/agent/chat-mcp.js.map +1 -0
- package/dist/agent/chat-tool-blocks.js +453 -0
- package/dist/agent/chat-tool-blocks.js.map +1 -0
- package/dist/agent/chat-view.js +94 -0
- package/dist/agent/chat-view.js.map +1 -0
- package/dist/agent/chat-views.js +781 -0
- package/dist/agent/chat-views.js.map +1 -0
- package/dist/agent/chat.js +1703 -0
- package/dist/agent/chat.js.map +1 -0
- package/dist/agent/destinations.js +150 -0
- package/dist/agent/destinations.js.map +1 -0
- package/dist/agent/llm-cli.js +451 -0
- package/dist/agent/llm-cli.js.map +1 -0
- package/dist/agent/llm-codex.js +493 -0
- package/dist/agent/llm-codex.js.map +1 -0
- package/dist/agent/llm-openai.js +211 -0
- package/dist/agent/llm-openai.js.map +1 -0
- package/dist/agent/llm.js +1464 -0
- package/dist/agent/llm.js.map +1 -0
- package/dist/agent/memory.js +33 -0
- package/dist/agent/memory.js.map +1 -0
- package/dist/agent/mock-views.js +93 -0
- package/dist/agent/mock-views.js.map +1 -0
- package/dist/agent/model-availability.js +207 -0
- package/dist/agent/model-availability.js.map +1 -0
- package/dist/agent/model-errors.js +188 -0
- package/dist/agent/model-errors.js.map +1 -0
- package/dist/agent/model-guard.js +311 -0
- package/dist/agent/model-guard.js.map +1 -0
- package/dist/agent/prompts.js +374 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/qa-lenses/qa-auth.md +32 -0
- package/dist/agent/qa-lenses/qa-check-then-act.md +33 -0
- package/dist/agent/qa-lenses/qa-control-has-no-effect.md +32 -0
- package/dist/agent/qa-lenses/qa-dates.md +31 -0
- package/dist/agent/qa-lenses/qa-double-submit.md +34 -0
- package/dist/agent/qa-lenses/qa-feature-flag.md +29 -0
- package/dist/agent/qa-lenses/qa-migration.md +35 -0
- package/dist/agent/qa-lenses/qa-money.md +38 -0
- package/dist/agent/qa-lenses/qa-response-shape-break.md +33 -0
- package/dist/agent/qa-lenses/qa-validation.md +29 -0
- package/dist/agent/qa-prompts.js +660 -0
- package/dist/agent/qa-prompts.js.map +1 -0
- package/dist/agent/reasoning.js +55 -0
- package/dist/agent/reasoning.js.map +1 -0
- package/dist/agent/redact.js +48 -0
- package/dist/agent/redact.js.map +1 -0
- package/dist/agent/rules.js +927 -0
- package/dist/agent/rules.js.map +1 -0
- package/dist/agent/runs.js +440 -0
- package/dist/agent/runs.js.map +1 -0
- package/dist/agent/send-retry.js +62 -0
- package/dist/agent/send-retry.js.map +1 -0
- package/dist/agent/team-notes.js +288 -0
- package/dist/agent/team-notes.js.map +1 -0
- package/dist/agent/token-meter.js +31 -0
- package/dist/agent/token-meter.js.map +1 -0
- package/dist/agent/triage.js +506 -0
- package/dist/agent/triage.js.map +1 -0
- package/dist/api.js +1047 -0
- package/dist/api.js.map +1 -0
- package/dist/approval-targets.js +107 -0
- package/dist/approval-targets.js.map +1 -0
- package/dist/bee/cloudflared.js +108 -0
- package/dist/bee/cloudflared.js.map +1 -0
- package/dist/bee/health.js +99 -0
- package/dist/bee/health.js.map +1 -0
- package/dist/bee/hive-client.js +75 -0
- package/dist/bee/hive-client.js.map +1 -0
- package/dist/bee/identity.js +39 -0
- package/dist/bee/identity.js.map +1 -0
- package/dist/bee/models.js +510 -0
- package/dist/bee/models.js.map +1 -0
- package/dist/bee/pairing.js +351 -0
- package/dist/bee/pairing.js.map +1 -0
- package/dist/bee/relay-client.js +50 -0
- package/dist/bee/relay-client.js.map +1 -0
- package/dist/bee/routes.js +214 -0
- package/dist/bee/routes.js.map +1 -0
- package/dist/bee/setup-page.js +223 -0
- package/dist/bee/setup-page.js.map +1 -0
- package/dist/bee/state.js +41 -0
- package/dist/bee/state.js.map +1 -0
- package/dist/bee/status.js +117 -0
- package/dist/bee/status.js.map +1 -0
- package/dist/bee/tunnel-manager.js +245 -0
- package/dist/bee/tunnel-manager.js.map +1 -0
- package/dist/bee/updater.js +268 -0
- package/dist/bee/updater.js.map +1 -0
- package/dist/boot.js +41 -0
- package/dist/boot.js.map +1 -0
- package/dist/call/index.js +145 -0
- package/dist/call/index.js.map +1 -0
- package/dist/case-folders.js +294 -0
- package/dist/case-folders.js.map +1 -0
- package/dist/cases.js +554 -0
- package/dist/cases.js.map +1 -0
- package/dist/chat-blocks.js +709 -0
- package/dist/chat-blocks.js.map +1 -0
- package/dist/chat-parse-util.js +38 -0
- package/dist/chat-parse-util.js.map +1 -0
- package/dist/chat-results.js +96 -0
- package/dist/chat-results.js.map +1 -0
- package/dist/chat-stats.js +193 -0
- package/dist/chat-stats.js.map +1 -0
- package/dist/chat-view-specs.js +186 -0
- package/dist/chat-view-specs.js.map +1 -0
- package/dist/cli/doctor-ops.js +361 -0
- package/dist/cli/doctor-ops.js.map +1 -0
- package/dist/cli/doctor.js +289 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/dogfood.js +70 -0
- package/dist/cli/dogfood.js.map +1 -0
- package/dist/cli/env-file.js +34 -0
- package/dist/cli/env-file.js.map +1 -0
- package/dist/cli/eval-chat-cases.js +359 -0
- package/dist/cli/eval-chat-cases.js.map +1 -0
- package/dist/cli/eval-chat-results.js +85 -0
- package/dist/cli/eval-chat-results.js.map +1 -0
- package/dist/cli/eval-chat.js +508 -0
- package/dist/cli/eval-chat.js.map +1 -0
- package/dist/cli/eval-designer.js +67 -0
- package/dist/cli/eval-designer.js.map +1 -0
- package/dist/cli/eval-qa-review.js +284 -0
- package/dist/cli/eval-qa-review.js.map +1 -0
- package/dist/cli/golden-export.js +35 -0
- package/dist/cli/golden-export.js.map +1 -0
- package/dist/cli/index.js +90 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.js +746 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/scan-repo.js +199 -0
- package/dist/cli/scan-repo.js.map +1 -0
- package/dist/cli/seed.js +108 -0
- package/dist/cli/seed.js.map +1 -0
- package/dist/cli/tinybee.js +247 -0
- package/dist/cli/tinybee.js.map +1 -0
- package/dist/config.js +138 -0
- package/dist/config.js.map +1 -0
- package/dist/connect/aivision-local.js +833 -0
- package/dist/connect/aivision-local.js.map +1 -0
- package/dist/connect/aivision-token.js +84 -0
- package/dist/connect/aivision-token.js.map +1 -0
- package/dist/connect/crypto.js +65 -0
- package/dist/connect/crypto.js.map +1 -0
- package/dist/connect/deps.js +110 -0
- package/dist/connect/deps.js.map +1 -0
- package/dist/connect/engines.js +144 -0
- package/dist/connect/engines.js.map +1 -0
- package/dist/connect/github-access.js +74 -0
- package/dist/connect/github-access.js.map +1 -0
- package/dist/connect/github-app.js +129 -0
- package/dist/connect/github-app.js.map +1 -0
- package/dist/connect/index.js +9 -0
- package/dist/connect/index.js.map +1 -0
- package/dist/connect/model-choices.js +201 -0
- package/dist/connect/model-choices.js.map +1 -0
- package/dist/connect/providers.js +900 -0
- package/dist/connect/providers.js.map +1 -0
- package/dist/connect/readiness.js +232 -0
- package/dist/connect/readiness.js.map +1 -0
- package/dist/connect/redact.js +16 -0
- package/dist/connect/redact.js.map +1 -0
- package/dist/connect/registry.js +406 -0
- package/dist/connect/registry.js.map +1 -0
- package/dist/connect/store.js +283 -0
- package/dist/connect/store.js.map +1 -0
- package/dist/connect/tunnel.js +134 -0
- package/dist/connect/tunnel.js.map +1 -0
- package/dist/connect/types.js +8 -0
- package/dist/connect/types.js.map +1 -0
- package/dist/connections/index.js +5 -0
- package/dist/connections/index.js.map +1 -0
- package/dist/connections/manager.js +223 -0
- package/dist/connections/manager.js.map +1 -0
- package/dist/connections/presets.js +264 -0
- package/dist/connections/presets.js.map +1 -0
- package/dist/connections/store.js +132 -0
- package/dist/connections/store.js.map +1 -0
- package/dist/connections/tools.js +206 -0
- package/dist/connections/tools.js.map +1 -0
- package/dist/db-schema.js +355 -0
- package/dist/db-schema.js.map +1 -0
- package/dist/db.js +379 -0
- package/dist/db.js.map +1 -0
- package/dist/delivery.js +49 -0
- package/dist/delivery.js.map +1 -0
- package/dist/deploy-wait.js +701 -0
- package/dist/deploy-wait.js.map +1 -0
- package/dist/disk.js +243 -0
- package/dist/disk.js.map +1 -0
- package/dist/dot-health.js +966 -0
- package/dist/dot-health.js.map +1 -0
- package/dist/dot-remove.js +246 -0
- package/dist/dot-remove.js.map +1 -0
- package/dist/events.js +38 -0
- package/dist/events.js.map +1 -0
- package/dist/evidence-pack.js +423 -0
- package/dist/evidence-pack.js.map +1 -0
- package/dist/flows/answers.js +289 -0
- package/dist/flows/answers.js.map +1 -0
- package/dist/flows/change.js +811 -0
- package/dist/flows/change.js.map +1 -0
- package/dist/flows/checkStatus.js +524 -0
- package/dist/flows/checkStatus.js.map +1 -0
- package/dist/flows/ci.js +236 -0
- package/dist/flows/ci.js.map +1 -0
- package/dist/flows/ciFailure.js +76 -0
- package/dist/flows/ciFailure.js.map +1 -0
- package/dist/flows/common.js +522 -0
- package/dist/flows/common.js.map +1 -0
- package/dist/flows/default-branch.js +29 -0
- package/dist/flows/default-branch.js.map +1 -0
- package/dist/flows/defectDraft.js +447 -0
- package/dist/flows/defectDraft.js.map +1 -0
- package/dist/flows/defects.js +623 -0
- package/dist/flows/defects.js.map +1 -0
- package/dist/flows/digest.js +295 -0
- package/dist/flows/digest.js.map +1 -0
- package/dist/flows/impact.js +82 -0
- package/dist/flows/impact.js.map +1 -0
- package/dist/flows/index.js +23 -0
- package/dist/flows/index.js.map +1 -0
- package/dist/flows/intent-sources.js +72 -0
- package/dist/flows/intent-sources.js.map +1 -0
- package/dist/flows/mergeCases.js +169 -0
- package/dist/flows/mergeCases.js.map +1 -0
- package/dist/flows/nightlyRun.js +82 -0
- package/dist/flows/nightlyRun.js.map +1 -0
- package/dist/flows/outage.js +64 -0
- package/dist/flows/outage.js.map +1 -0
- package/dist/flows/postMerge.js +5 -0
- package/dist/flows/postMerge.js.map +1 -0
- package/dist/flows/prCheck.js +5 -0
- package/dist/flows/prCheck.js.map +1 -0
- package/dist/flows/prTarget.js +66 -0
- package/dist/flows/prTarget.js.map +1 -0
- package/dist/flows/qa.js +363 -0
- package/dist/flows/qa.js.map +1 -0
- package/dist/flows/questions.js +160 -0
- package/dist/flows/questions.js.map +1 -0
- package/dist/flows/recovery.js +64 -0
- package/dist/flows/recovery.js.map +1 -0
- package/dist/flows/rerun.js +50 -0
- package/dist/flows/rerun.js.map +1 -0
- package/dist/flows/testDesign.js +294 -0
- package/dist/flows/testDesign.js.map +1 -0
- package/dist/flows/testDesignJob.js +46 -0
- package/dist/flows/testDesignJob.js.map +1 -0
- package/dist/flows/testRepo.js +228 -0
- package/dist/flows/testRepo.js.map +1 -0
- package/dist/flows/testSavings.js +381 -0
- package/dist/flows/testSavings.js.map +1 -0
- package/dist/flows/testStatus.js +343 -0
- package/dist/flows/testStatus.js.map +1 -0
- package/dist/flows/ticketContext.js +425 -0
- package/dist/flows/ticketContext.js.map +1 -0
- package/dist/flows/ticketReview.js +249 -0
- package/dist/flows/ticketReview.js.map +1 -0
- package/dist/flows/ticks.js +73 -0
- package/dist/flows/ticks.js.map +1 -0
- package/dist/hive/admin.js +122 -0
- package/dist/hive/admin.js.map +1 -0
- package/dist/hive/auth.js +130 -0
- package/dist/hive/auth.js.map +1 -0
- package/dist/hive/billing.js +278 -0
- package/dist/hive/billing.js.map +1 -0
- package/dist/hive/config.js +63 -0
- package/dist/hive/config.js.map +1 -0
- package/dist/hive/db.js +186 -0
- package/dist/hive/db.js.map +1 -0
- package/dist/hive/embed.js +76 -0
- package/dist/hive/embed.js.map +1 -0
- package/dist/hive/errors.js +17 -0
- package/dist/hive/errors.js.map +1 -0
- package/dist/hive/google.js +75 -0
- package/dist/hive/google.js.map +1 -0
- package/dist/hive/index.js +25 -0
- package/dist/hive/index.js.map +1 -0
- package/dist/hive/jwt.js +57 -0
- package/dist/hive/jwt.js.map +1 -0
- package/dist/hive/keys.js +61 -0
- package/dist/hive/keys.js.map +1 -0
- package/dist/hive/mail.js +27 -0
- package/dist/hive/mail.js.map +1 -0
- package/dist/hive/native-push.js +255 -0
- package/dist/hive/native-push.js.map +1 -0
- package/dist/hive/pairing.js +342 -0
- package/dist/hive/pairing.js.map +1 -0
- package/dist/hive/presence.js +121 -0
- package/dist/hive/presence.js.map +1 -0
- package/dist/hive/push.js +185 -0
- package/dist/hive/push.js.map +1 -0
- package/dist/hive/relay.js +205 -0
- package/dist/hive/relay.js.map +1 -0
- package/dist/hive/routes.js +228 -0
- package/dist/hive/routes.js.map +1 -0
- package/dist/hive/server.js +12 -0
- package/dist/hive/server.js.map +1 -0
- package/dist/hive/session.js +28 -0
- package/dist/hive/session.js.map +1 -0
- package/dist/hive/tenants.js +287 -0
- package/dist/hive/tenants.js.map +1 -0
- package/dist/hive/tunnel/cloudflare.js +160 -0
- package/dist/hive/tunnel/cloudflare.js.map +1 -0
- package/dist/hive/tunnel/mock.js +52 -0
- package/dist/hive/tunnel/mock.js.map +1 -0
- package/dist/hive/tunnel/provider.js +19 -0
- package/dist/hive/tunnel/provider.js.map +1 -0
- package/dist/hive/updates.js +61 -0
- package/dist/hive/updates.js.map +1 -0
- package/dist/http-error.js +11 -0
- package/dist/http-error.js.map +1 -0
- package/dist/impact/classify.js +226 -0
- package/dist/impact/classify.js.map +1 -0
- package/dist/impact/find.js +259 -0
- package/dist/impact/find.js.map +1 -0
- package/dist/impact/pr.js +54 -0
- package/dist/impact/pr.js.map +1 -0
- package/dist/impact/store.js +365 -0
- package/dist/impact/store.js.map +1 -0
- package/dist/impact/text.js +158 -0
- package/dist/impact/text.js.map +1 -0
- package/dist/index.js +288 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/adf.js +397 -0
- package/dist/integrations/adf.js.map +1 -0
- package/dist/integrations/bot-marker.js +103 -0
- package/dist/integrations/bot-marker.js.map +1 -0
- package/dist/integrations/github-issues.js +705 -0
- package/dist/integrations/github-issues.js.map +1 -0
- package/dist/integrations/github.js +1284 -0
- package/dist/integrations/github.js.map +1 -0
- package/dist/integrations/issue-create.js +60 -0
- package/dist/integrations/issue-create.js.map +1 -0
- package/dist/integrations/jira.js +279 -0
- package/dist/integrations/jira.js.map +1 -0
- package/dist/integrations/linear.js +273 -0
- package/dist/integrations/linear.js.map +1 -0
- package/dist/integrations/own-identity-warm.js +47 -0
- package/dist/integrations/own-identity-warm.js.map +1 -0
- package/dist/integrations/own-identity.js +39 -0
- package/dist/integrations/own-identity.js.map +1 -0
- package/dist/integrations/status-change.js +42 -0
- package/dist/integrations/status-change.js.map +1 -0
- package/dist/lead-defects.js +202 -0
- package/dist/lead-defects.js.map +1 -0
- package/dist/lead-digest.js +333 -0
- package/dist/lead-digest.js.map +1 -0
- package/dist/lead-release.js +286 -0
- package/dist/lead-release.js.map +1 -0
- package/dist/lead.js +328 -0
- package/dist/lead.js.map +1 -0
- package/dist/legacy-names.js +51 -0
- package/dist/legacy-names.js.map +1 -0
- package/dist/limits.js +44 -0
- package/dist/limits.js.map +1 -0
- package/dist/link-trust.js +224 -0
- package/dist/link-trust.js.map +1 -0
- package/dist/links.js +219 -0
- package/dist/links.js.map +1 -0
- package/dist/loopback.js +35 -0
- package/dist/loopback.js.map +1 -0
- package/dist/model-cap.js +60 -0
- package/dist/model-cap.js.map +1 -0
- package/dist/model-usage.js +173 -0
- package/dist/model-usage.js.map +1 -0
- package/dist/monitor/backfill.js +39 -0
- package/dist/monitor/backfill.js.map +1 -0
- package/dist/monitor/browser-probe.js +198 -0
- package/dist/monitor/browser-probe.js.map +1 -0
- package/dist/monitor/catch-up.js +31 -0
- package/dist/monitor/catch-up.js.map +1 -0
- package/dist/monitor/pollers.js +1606 -0
- package/dist/monitor/pollers.js.map +1 -0
- package/dist/monitor/probes.js +178 -0
- package/dist/monitor/probes.js.map +1 -0
- package/dist/monitor/self-health.js +228 -0
- package/dist/monitor/self-health.js.map +1 -0
- package/dist/monitor/statuspage.js +105 -0
- package/dist/monitor/statuspage.js.map +1 -0
- package/dist/nightly-scheduler.js +127 -0
- package/dist/nightly-scheduler.js.map +1 -0
- package/dist/nightly.js +141 -0
- package/dist/nightly.js.map +1 -0
- package/dist/notify/headline.js +114 -0
- package/dist/notify/headline.js.map +1 -0
- package/dist/notify/push.js +174 -0
- package/dist/notify/push.js.map +1 -0
- package/dist/object-cards.js +585 -0
- package/dist/object-cards.js.map +1 -0
- package/dist/object-links.js +47 -0
- package/dist/object-links.js.map +1 -0
- package/dist/onboarding-checks.js +141 -0
- package/dist/onboarding-checks.js.map +1 -0
- package/dist/onboarding.js +401 -0
- package/dist/onboarding.js.map +1 -0
- package/dist/path-filters.js +62 -0
- package/dist/path-filters.js.map +1 -0
- package/dist/phone/alerts.js +67 -0
- package/dist/phone/alerts.js.map +1 -0
- package/dist/phone/auth.js +240 -0
- package/dist/phone/auth.js.map +1 -0
- package/dist/phone/pairing.js +442 -0
- package/dist/phone/pairing.js.map +1 -0
- package/dist/phone/prefs.js +27 -0
- package/dist/phone/prefs.js.map +1 -0
- package/dist/phone/tailscale.js +161 -0
- package/dist/phone/tailscale.js.map +1 -0
- package/dist/pr-scope.js +441 -0
- package/dist/pr-scope.js.map +1 -0
- package/dist/proc.js +156 -0
- package/dist/proc.js.map +1 -0
- package/dist/profile/cache.js +108 -0
- package/dist/profile/cache.js.map +1 -0
- package/dist/profile/ci.js +216 -0
- package/dist/profile/ci.js.map +1 -0
- package/dist/profile/index.js +8 -0
- package/dist/profile/index.js.map +1 -0
- package/dist/profile/job.js +135 -0
- package/dist/profile/job.js.map +1 -0
- package/dist/profile/queries/go-tags.scm +47 -0
- package/dist/profile/queries/java-tags.scm +25 -0
- package/dist/profile/queries/javascript-tags.scm +104 -0
- package/dist/profile/queries/python-tags.scm +19 -0
- package/dist/profile/queries/typescript-tags.scm +28 -0
- package/dist/profile/scan-thread.js +42 -0
- package/dist/profile/scan-thread.js.map +1 -0
- package/dist/profile/scan-worker.js +16 -0
- package/dist/profile/scan-worker.js.map +1 -0
- package/dist/profile/scan.js +351 -0
- package/dist/profile/scan.js.map +1 -0
- package/dist/profile/stack.js +528 -0
- package/dist/profile/stack.js.map +1 -0
- package/dist/profile/store.js +35 -0
- package/dist/profile/store.js.map +1 -0
- package/dist/profile/tests.js +399 -0
- package/dist/profile/tests.js.map +1 -0
- package/dist/queue.js +350 -0
- package/dist/queue.js.map +1 -0
- package/dist/refs.js +756 -0
- package/dist/refs.js.map +1 -0
- package/dist/release-notes.js +272 -0
- package/dist/release-notes.js.map +1 -0
- package/dist/reports/build.js +243 -0
- package/dist/reports/build.js.map +1 -0
- package/dist/reports/index.js +211 -0
- package/dist/reports/index.js.map +1 -0
- package/dist/reports/store.js +150 -0
- package/dist/reports/store.js.map +1 -0
- package/dist/reports/ticket-titles.js +124 -0
- package/dist/reports/ticket-titles.js.map +1 -0
- package/dist/reports/trace-map.js +1278 -0
- package/dist/reports/trace-map.js.map +1 -0
- package/dist/reports/traceability.js +422 -0
- package/dist/reports/traceability.js.map +1 -0
- package/dist/repos/mirror.js +356 -0
- package/dist/repos/mirror.js.map +1 -0
- package/dist/repos-github.js +108 -0
- package/dist/repos-github.js.map +1 -0
- package/dist/repos.js +891 -0
- package/dist/repos.js.map +1 -0
- package/dist/review/backend-anthropic.js +90 -0
- package/dist/review/backend-anthropic.js.map +1 -0
- package/dist/review/backend-cli.js +248 -0
- package/dist/review/backend-cli.js.map +1 -0
- package/dist/review/backend-codex.js +81 -0
- package/dist/review/backend-codex.js.map +1 -0
- package/dist/review/backend-mock.js +686 -0
- package/dist/review/backend-mock.js.map +1 -0
- package/dist/review/backend-only.js +168 -0
- package/dist/review/backend-only.js.map +1 -0
- package/dist/review/backend.js +105 -0
- package/dist/review/backend.js.map +1 -0
- package/dist/review/baseline/render-v1.js +483 -0
- package/dist/review/baseline/render-v1.js.map +1 -0
- package/dist/review/change-summary.js +197 -0
- package/dist/review/change-summary.js.map +1 -0
- package/dist/review/changelog.js +40 -0
- package/dist/review/changelog.js.map +1 -0
- package/dist/review/check.js +494 -0
- package/dist/review/check.js.map +1 -0
- package/dist/review/checkout.js +185 -0
- package/dist/review/checkout.js.map +1 -0
- package/dist/review/classify.js +178 -0
- package/dist/review/classify.js.map +1 -0
- package/dist/review/coverage.js +347 -0
- package/dist/review/coverage.js.map +1 -0
- package/dist/review/decision-eval.js +121 -0
- package/dist/review/decision-eval.js.map +1 -0
- package/dist/review/deliver.js +104 -0
- package/dist/review/deliver.js.map +1 -0
- package/dist/review/designer-eval.js +280 -0
- package/dist/review/designer-eval.js.map +1 -0
- package/dist/review/dogfood-digest.js +136 -0
- package/dist/review/dogfood-digest.js.map +1 -0
- package/dist/review/dogfood-eval.js +486 -0
- package/dist/review/dogfood-eval.js.map +1 -0
- package/dist/review/environment.js +109 -0
- package/dist/review/environment.js.map +1 -0
- package/dist/review/eval.js +405 -0
- package/dist/review/eval.js.map +1 -0
- package/dist/review/glob.js +110 -0
- package/dist/review/glob.js.map +1 -0
- package/dist/review/golden-export.js +219 -0
- package/dist/review/golden-export.js.map +1 -0
- package/dist/review/impact.js +406 -0
- package/dist/review/impact.js.map +1 -0
- package/dist/review/imports.js +542 -0
- package/dist/review/imports.js.map +1 -0
- package/dist/review/index.js +512 -0
- package/dist/review/index.js.map +1 -0
- package/dist/review/intent.js +128 -0
- package/dist/review/intent.js.map +1 -0
- package/dist/review/lenses.js +167 -0
- package/dist/review/lenses.js.map +1 -0
- package/dist/review/linked-tickets.js +293 -0
- package/dist/review/linked-tickets.js.map +1 -0
- package/dist/review/merge.js +370 -0
- package/dist/review/merge.js.map +1 -0
- package/dist/review/on-demand.js +218 -0
- package/dist/review/on-demand.js.map +1 -0
- package/dist/review/prompt-eval.js +299 -0
- package/dist/review/prompt-eval.js.map +1 -0
- package/dist/review/prompt-variants.js +37 -0
- package/dist/review/prompt-variants.js.map +1 -0
- package/dist/review/readability-eval.js +217 -0
- package/dist/review/readability-eval.js.map +1 -0
- package/dist/review/readability.js +295 -0
- package/dist/review/readability.js.map +1 -0
- package/dist/review/render.js +2409 -0
- package/dist/review/render.js.map +1 -0
- package/dist/review/repo.js +194 -0
- package/dist/review/repo.js.map +1 -0
- package/dist/review/report-plan.js +90 -0
- package/dist/review/report-plan.js.map +1 -0
- package/dist/review/routes.js +195 -0
- package/dist/review/routes.js.map +1 -0
- package/dist/review/run-summary.js +34 -0
- package/dist/review/run-summary.js.map +1 -0
- package/dist/review/run.js +245 -0
- package/dist/review/run.js.map +1 -0
- package/dist/review/scope.js +454 -0
- package/dist/review/scope.js.map +1 -0
- package/dist/review/security.js +432 -0
- package/dist/review/security.js.map +1 -0
- package/dist/review/signals.js +550 -0
- package/dist/review/signals.js.map +1 -0
- package/dist/review/specs.js +150 -0
- package/dist/review/specs.js.map +1 -0
- package/dist/review/ste.js +133 -0
- package/dist/review/ste.js.map +1 -0
- package/dist/review/test-repo-file.js +18 -0
- package/dist/review/test-repo-file.js.map +1 -0
- package/dist/review/test-repo-lines.js +53 -0
- package/dist/review/test-repo-lines.js.map +1 -0
- package/dist/review/ticket-on-demand.js +232 -0
- package/dist/review/ticket-on-demand.js.map +1 -0
- package/dist/review/yaml.js +298 -0
- package/dist/review/yaml.js.map +1 -0
- package/dist/room.js +361 -0
- package/dist/room.js.map +1 -0
- package/dist/routes/activity.js +219 -0
- package/dist/routes/activity.js.map +1 -0
- package/dist/routes/call.js +29 -0
- package/dist/routes/call.js.map +1 -0
- package/dist/routes/cards.js +40 -0
- package/dist/routes/cards.js.map +1 -0
- package/dist/routes/cases.js +133 -0
- package/dist/routes/cases.js.map +1 -0
- package/dist/routes/connect.js +268 -0
- package/dist/routes/connect.js.map +1 -0
- package/dist/routes/connection-presets.js +32 -0
- package/dist/routes/connection-presets.js.map +1 -0
- package/dist/routes/connections.js +177 -0
- package/dist/routes/connections.js.map +1 -0
- package/dist/routes/disk.js +19 -0
- package/dist/routes/disk.js.map +1 -0
- package/dist/routes/docs.js +71 -0
- package/dist/routes/docs.js.map +1 -0
- package/dist/routes/dot-suggest.js +1002 -0
- package/dist/routes/dot-suggest.js.map +1 -0
- package/dist/routes/evidence-pack.js +16 -0
- package/dist/routes/evidence-pack.js.map +1 -0
- package/dist/routes/health.js +121 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/impact.js +48 -0
- package/dist/routes/impact.js.map +1 -0
- package/dist/routes/lead.js +50 -0
- package/dist/routes/lead.js.map +1 -0
- package/dist/routes/links.js +13 -0
- package/dist/routes/links.js.map +1 -0
- package/dist/routes/markdown.js +62 -0
- package/dist/routes/markdown.js.map +1 -0
- package/dist/routes/onboarding.js +47 -0
- package/dist/routes/onboarding.js.map +1 -0
- package/dist/routes/phone.js +231 -0
- package/dist/routes/phone.js.map +1 -0
- package/dist/routes/release-notes.js +30 -0
- package/dist/routes/release-notes.js.map +1 -0
- package/dist/routes/reports.js +102 -0
- package/dist/routes/reports.js.map +1 -0
- package/dist/routes/repos.js +91 -0
- package/dist/routes/repos.js.map +1 -0
- package/dist/routes/review.js +283 -0
- package/dist/routes/review.js.map +1 -0
- package/dist/routes/roles.js +114 -0
- package/dist/routes/roles.js.map +1 -0
- package/dist/routes/room.js +21 -0
- package/dist/routes/room.js.map +1 -0
- package/dist/routes/runner.js +156 -0
- package/dist/routes/runner.js.map +1 -0
- package/dist/routes/runs.js +84 -0
- package/dist/routes/runs.js.map +1 -0
- package/dist/routes/setup.js +18 -0
- package/dist/routes/setup.js.map +1 -0
- package/dist/routes/test-runs.js +220 -0
- package/dist/routes/test-runs.js.map +1 -0
- package/dist/routes/ticket-status.js +17 -0
- package/dist/routes/ticket-status.js.map +1 -0
- package/dist/routes/traceability.js +143 -0
- package/dist/routes/traceability.js.map +1 -0
- package/dist/routes/true.js +142 -0
- package/dist/routes/true.js.map +1 -0
- package/dist/routes/updates.js +70 -0
- package/dist/routes/updates.js.map +1 -0
- package/dist/runner/adapters/index.js +12 -0
- package/dist/runner/adapters/index.js.map +1 -0
- package/dist/runner/adapters/playwright.js +68 -0
- package/dist/runner/adapters/playwright.js.map +1 -0
- package/dist/runner/aivision/cases.js +172 -0
- package/dist/runner/aivision/cases.js.map +1 -0
- package/dist/runner/aivision/cli.js +201 -0
- package/dist/runner/aivision/cli.js.map +1 -0
- package/dist/runner/aivision/index.js +281 -0
- package/dist/runner/aivision/index.js.map +1 -0
- package/dist/runner/aivision/server.js +394 -0
- package/dist/runner/aivision/server.js.map +1 -0
- package/dist/runner/aivision/shared.js +140 -0
- package/dist/runner/aivision/shared.js.map +1 -0
- package/dist/runner/artifact-cap.js +189 -0
- package/dist/runner/artifact-cap.js.map +1 -0
- package/dist/runner/artifacts.js +433 -0
- package/dist/runner/artifacts.js.map +1 -0
- package/dist/runner/command.js +564 -0
- package/dist/runner/command.js.map +1 -0
- package/dist/runner/copyfiles.js +221 -0
- package/dist/runner/copyfiles.js.map +1 -0
- package/dist/runner/engines.js +32 -0
- package/dist/runner/engines.js.map +1 -0
- package/dist/runner/evidence.js +114 -0
- package/dist/runner/evidence.js.map +1 -0
- package/dist/runner/host-env.js +160 -0
- package/dist/runner/host-env.js.map +1 -0
- package/dist/runner/index.js +1221 -0
- package/dist/runner/index.js.map +1 -0
- package/dist/runner/observe.js +603 -0
- package/dist/runner/observe.js.map +1 -0
- package/dist/runner/playwright-json.js +108 -0
- package/dist/runner/playwright-json.js.map +1 -0
- package/dist/runner/pwgen/exec.js +192 -0
- package/dist/runner/pwgen/exec.js.map +1 -0
- package/dist/runner/pwgen/explore.js +111 -0
- package/dist/runner/pwgen/explore.js.map +1 -0
- package/dist/runner/pwgen/index.js +423 -0
- package/dist/runner/pwgen/index.js.map +1 -0
- package/dist/runner/pwgen/propose.js +69 -0
- package/dist/runner/pwgen/propose.js.map +1 -0
- package/dist/runner/pwgen/source.js +632 -0
- package/dist/runner/pwgen/source.js.map +1 -0
- package/dist/runner/setup.js +222 -0
- package/dist/runner/setup.js.map +1 -0
- package/dist/runner/step-reporter.js +107 -0
- package/dist/runner/step-reporter.js.map +1 -0
- package/dist/runner/test-repo.js +326 -0
- package/dist/runner/test-repo.js.map +1 -0
- package/dist/runner/typed-value.js +46 -0
- package/dist/runner/typed-value.js.map +1 -0
- package/dist/runner/verify.js +133 -0
- package/dist/runner/verify.js.map +1 -0
- package/dist/runner/worktree.js +349 -0
- package/dist/runner/worktree.js.map +1 -0
- package/dist/sandbox.js +61 -0
- package/dist/sandbox.js.map +1 -0
- package/dist/server.js +262 -0
- package/dist/server.js.map +1 -0
- package/dist/shadow-week.js +126 -0
- package/dist/shadow-week.js.map +1 -0
- package/dist/shutdown.js +92 -0
- package/dist/shutdown.js.map +1 -0
- package/dist/slack/index.js +645 -0
- package/dist/slack/index.js.map +1 -0
- package/dist/stages/config.js +147 -0
- package/dist/stages/config.js.map +1 -0
- package/dist/stages/index.js +41 -0
- package/dist/stages/index.js.map +1 -0
- package/dist/suites.js +293 -0
- package/dist/suites.js.map +1 -0
- package/dist/target-url.js +116 -0
- package/dist/target-url.js.map +1 -0
- package/dist/test-runs.js +662 -0
- package/dist/test-runs.js.map +1 -0
- package/dist/ticket-keys.js +97 -0
- package/dist/ticket-keys.js.map +1 -0
- package/dist/ticket-supersede.js +105 -0
- package/dist/ticket-supersede.js.map +1 -0
- package/dist/tinybee/ed25519.js +99 -0
- package/dist/tinybee/ed25519.js.map +1 -0
- package/dist/tinybee/models-routes.js +19 -0
- package/dist/tinybee/models-routes.js.map +1 -0
- package/dist/tinybee/pair-payload.js +41 -0
- package/dist/tinybee/pair-payload.js.map +1 -0
- package/dist/tinybee/update-manifest.js +77 -0
- package/dist/tinybee/update-manifest.js.map +1 -0
- package/dist/tinybee/well-known.js +64 -0
- package/dist/tinybee/well-known.js.map +1 -0
- package/dist/tinybee/workflows/activity.js +23 -0
- package/dist/tinybee/workflows/activity.js.map +1 -0
- package/dist/tinybee/workflows/cron.js +210 -0
- package/dist/tinybee/workflows/cron.js.map +1 -0
- package/dist/tinybee/workflows/engine.js +371 -0
- package/dist/tinybee/workflows/engine.js.map +1 -0
- package/dist/tinybee/workflows/from-text.js +203 -0
- package/dist/tinybee/workflows/from-text.js.map +1 -0
- package/dist/tinybee/workflows/routes.js +189 -0
- package/dist/tinybee/workflows/routes.js.map +1 -0
- package/dist/tinybee/workflows/scheduler.js +100 -0
- package/dist/tinybee/workflows/scheduler.js.map +1 -0
- package/dist/tinybee/workflows/skills.js +227 -0
- package/dist/tinybee/workflows/skills.js.map +1 -0
- package/dist/tinybee/workflows/store.js +205 -0
- package/dist/tinybee/workflows/store.js.map +1 -0
- package/dist/tinybee/workflows/types.js +180 -0
- package/dist/tinybee/workflows/types.js.map +1 -0
- package/dist/tinybee/workflows/yaml.js +323 -0
- package/dist/tinybee/workflows/yaml.js.map +1 -0
- package/dist/true/api.js +495 -0
- package/dist/true/api.js.map +1 -0
- package/dist/true/client.js +292 -0
- package/dist/true/client.js.map +1 -0
- package/dist/true/clock.js +36 -0
- package/dist/true/clock.js.map +1 -0
- package/dist/true/events.js +125 -0
- package/dist/true/events.js.map +1 -0
- package/dist/true/hosts.js +98 -0
- package/dist/true/hosts.js.map +1 -0
- package/dist/true/index.js +29 -0
- package/dist/true/index.js.map +1 -0
- package/dist/true/key.js +54 -0
- package/dist/true/key.js.map +1 -0
- package/dist/true/line.js +69 -0
- package/dist/true/line.js.map +1 -0
- package/dist/true/mask.js +31 -0
- package/dist/true/mask.js.map +1 -0
- package/dist/true/names.js +114 -0
- package/dist/true/names.js.map +1 -0
- package/dist/true/outbox.js +233 -0
- package/dist/true/outbox.js.map +1 -0
- package/dist/true/replay.js +164 -0
- package/dist/true/replay.js.map +1 -0
- package/dist/true/settings.js +123 -0
- package/dist/true/settings.js.map +1 -0
- package/dist/true/status.js +189 -0
- package/dist/true/status.js.map +1 -0
- package/dist/true/sync-cases.js +222 -0
- package/dist/true/sync-cases.js.map +1 -0
- package/dist/true/sync-manual.js +295 -0
- package/dist/true/sync-manual.js.map +1 -0
- package/dist/true/sync-runs.js +109 -0
- package/dist/true/sync-runs.js.map +1 -0
- package/dist/true/worker.js +414 -0
- package/dist/true/worker.js.map +1 -0
- package/dist/types.js +32 -0
- package/dist/types.js.map +1 -0
- package/dist/updates/asks.js +38 -0
- package/dist/updates/asks.js.map +1 -0
- package/dist/updates/index.js +835 -0
- package/dist/updates/index.js.map +1 -0
- package/dist/updates/owner.js +24 -0
- package/dist/updates/owner.js.map +1 -0
- package/dist/updates/prefs.js +118 -0
- package/dist/updates/prefs.js.map +1 -0
- package/dist/updates/route.js +78 -0
- package/dist/updates/route.js.map +1 -0
- package/dist/updates/sources.js +345 -0
- package/dist/updates/sources.js.map +1 -0
- package/dist/updates/words.js +452 -0
- package/dist/updates/words.js.map +1 -0
- package/dist/version.js +53 -0
- package/dist/version.js.map +1 -0
- package/dist/view-compute.js +382 -0
- package/dist/view-compute.js.map +1 -0
- package/dist/view-spec.js +894 -0
- package/dist/view-spec.js.map +1 -0
- package/dist/web/apple-touch-icon.png +0 -0
- package/dist/web/assets/ApprovalCard-Br_z5z7G.css +1 -0
- package/dist/web/assets/ApprovalCard-CfGhGFo_.js +3 -0
- package/dist/web/assets/CardFromRef-BaTGmZ9c.js +2 -0
- package/dist/web/assets/CardFromRef-DO7i2uDd.css +1 -0
- package/dist/web/assets/Cases-8l_6UcLh.js +2 -0
- package/dist/web/assets/Cases-CpfED0uB.css +1 -0
- package/dist/web/assets/Chat-BKDiR0qt.js +3 -0
- package/dist/web/assets/Chat-DxjdyYUu.css +1 -0
- package/dist/web/assets/ConfirmDialog-CIMveD-b.js +2 -0
- package/dist/web/assets/ConnectSheet-Cm5vbZdR.js +4 -0
- package/dist/web/assets/Docs-HKGuY0WE.js +2 -0
- package/dist/web/assets/Docs-fodKx8S7.css +1 -0
- package/dist/web/assets/DotDetail-DskS3NWe.js +2 -0
- package/dist/web/assets/DotDetail-OWRSD_1x.css +1 -0
- package/dist/web/assets/DotStage-BZDclfzV.css +1 -0
- package/dist/web/assets/DotStage-CtMNzCN3.js +2 -0
- package/dist/web/assets/Dots-BqCsS34m.js +2 -0
- package/dist/web/assets/EvidencePack-DQ3kCJX4.js +2 -0
- package/dist/web/assets/EvidencePack-DV_9ETsA.css +1 -0
- package/dist/web/assets/FailedDeliveries-1F2kFcs9.js +2 -0
- package/dist/web/assets/FailedDeliveries-DXa2_VvI.css +1 -0
- package/dist/web/assets/Feed-5wiHRTFK.js +2 -0
- package/dist/web/assets/GfmView-C-NYaduC.js +4 -0
- package/dist/web/assets/ImpactList-C-RNuDB0.js +2 -0
- package/dist/web/assets/ImpactList-C612dFdw.css +1 -0
- package/dist/web/assets/Inbox-kc1bKVL_.js +2 -0
- package/dist/web/assets/JoinScreen-C3L4wmP3.js +2 -0
- package/dist/web/assets/JoinScreen-COeAUv5v.css +1 -0
- package/dist/web/assets/LeadDialog-N3Zul9Rw.js +8 -0
- package/dist/web/assets/LeadDialog-xyWjAuAS.css +1 -0
- package/dist/web/assets/ListeningLine-BRFNaczY.js +2 -0
- package/dist/web/assets/ListeningLine-pN8CgyiK.css +1 -0
- package/dist/web/assets/Mascots-pIyGpxtD.js +2 -0
- package/dist/web/assets/ModelChoices-BxO1zyyr.js +2 -0
- package/dist/web/assets/ModelChoices-DQJnVxfs.css +1 -0
- package/dist/web/assets/NewDotDialog-CDPUWmgN.js +2 -0
- package/dist/web/assets/NewDotDialog-DrPPqkSZ.css +1 -0
- package/dist/web/assets/NewTestRunDialog-BQBthVHE.js +2 -0
- package/dist/web/assets/NoBreakNames-Csxdqdt6.js +2 -0
- package/dist/web/assets/Pair-omex_K7H.js +2 -0
- package/dist/web/assets/PushSection-CmqHlE_2.js +2 -0
- package/dist/web/assets/ReleaseNotes-B6VqFewP.js +2 -0
- package/dist/web/assets/ReleaseNotes-DBJUL7Vm.css +1 -0
- package/dist/web/assets/ReportPage-BHS1xWEt.js +162 -0
- package/dist/web/assets/Reports-BEHnDIb1.js +2 -0
- package/dist/web/assets/ReviewChangeDialog-BVSt-cw-.js +2 -0
- package/dist/web/assets/ReviewChangeDialog-BbIlHRx0.css +1 -0
- package/dist/web/assets/Room-BoG_uge4.js +2 -0
- package/dist/web/assets/Room-evAfiW_e.css +1 -0
- package/dist/web/assets/RunActions-ZY7yf1AF.js +2 -0
- package/dist/web/assets/RunDetail-C8St20GQ.css +1 -0
- package/dist/web/assets/RunDetail-qADXjBvd.js +9 -0
- package/dist/web/assets/RunRow-BKEJv8ib.js +2 -0
- package/dist/web/assets/RunnerTry-Dgpp67-s.js +6 -0
- package/dist/web/assets/RunnerTry-DsweWOfO.css +1 -0
- package/dist/web/assets/Settings-fs-QJwNM.js +2 -0
- package/dist/web/assets/Setup-DI6hBEP-.js +2 -0
- package/dist/web/assets/TestRows-wh03T5Li.js +4 -0
- package/dist/web/assets/TestRun-DTOyF-Pv.js +2 -0
- package/dist/web/assets/TestRuns-ggDjm1Q8.js +2 -0
- package/dist/web/assets/TinyBeeAddLaptop-bc3pmh3T.js +2 -0
- package/dist/web/assets/TinyBeeAi-CkyOzhm5.js +2 -0
- package/dist/web/assets/TinyBeeConnections-B2K_P38u.js +2 -0
- package/dist/web/assets/TinyBeeHome-DSpxX5KS.js +3 -0
- package/dist/web/assets/TinyBeePlan-BamiLziu.js +2 -0
- package/dist/web/assets/TinyBeeSignup-vHcz0Wen.js +2 -0
- package/dist/web/assets/TinyBeeSkills-By9RLUse.js +2 -0
- package/dist/web/assets/TinyBeeWorkflow-DeNO7Jsh.js +2 -0
- package/dist/web/assets/TinyBeeWorkflowEditor-B6OGC2OT.js +2 -0
- package/dist/web/assets/TinyBeeWorkflows-BN4-h6lt.js +2 -0
- package/dist/web/assets/Trace-BBCI1aIB.css +1 -0
- package/dist/web/assets/Trace-aF92UWaH.js +2 -0
- package/dist/web/assets/UpdatesSection-CCWEbkue.js +2 -0
- package/dist/web/assets/Welcome-CCcy558Q.js +2 -0
- package/dist/web/assets/activity-C3qilrDm.css +1 -0
- package/dist/web/assets/activity-CBfTSxqw.js +2 -0
- package/dist/web/assets/authedMedia-BTYt_tyu.js +2 -0
- package/dist/web/assets/beeApi-BHW8Tx1s.js +2 -0
- package/dist/web/assets/data-CmXvx3Mj.js +2 -0
- package/dist/web/assets/engines-CfdJODFj.css +1 -0
- package/dist/web/assets/format-BBZ8vFyT.js +2 -0
- package/dist/web/assets/forms-Coc7p2UF.css +1 -0
- package/dist/web/assets/hivePush-DZJDokj7.js +2 -0
- package/dist/web/assets/impactModel-MY1OFuiP.js +2 -0
- package/dist/web/assets/index-B6qKm1NW.js +13 -0
- package/dist/web/assets/index-BvKyT1X7.css +1 -0
- package/dist/web/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/dist/web/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/dist/web/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/dist/web/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/dist/web/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/dist/web/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/dist/web/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/dist/web/assets/lead-BL_P_kri.css +1 -0
- package/dist/web/assets/model-BIQy5IVG.js +2 -0
- package/dist/web/assets/modelsApi-PUSySRQw.js +2 -0
- package/dist/web/assets/newDotModel-BaLLyw57.js +2 -0
- package/dist/web/assets/onboarding-bfKqnM4Z.css +1 -0
- package/dist/web/assets/phone-Rs7FoyG0.css +1 -0
- package/dist/web/assets/phoneApi-BG_1-M-D.js +3 -0
- package/dist/web/assets/releaseModel-EynYMVOC.js +2 -0
- package/dist/web/assets/releaseNotesModel-A1Rtv3cV.js +2 -0
- package/dist/web/assets/reports-BWaA1HhM.js +37 -0
- package/dist/web/assets/reports-DXwdWQsB.css +1 -0
- package/dist/web/assets/router-W6nZsQ33.js +2 -0
- package/dist/web/assets/runs-BY0Owv93.css +1 -0
- package/dist/web/assets/shadowWeekModel-BUUiww09.js +2 -0
- package/dist/web/assets/storage-yvL36AWt.js +2 -0
- package/dist/web/assets/store-IqrqHuFR.js +2 -0
- package/dist/web/assets/testruns-BzIZ0cR0.css +1 -0
- package/dist/web/assets/testruns-DyaqX4ji.js +2 -0
- package/dist/web/assets/true-9Q68cs5b.js +2 -0
- package/dist/web/assets/true-DPGBpoir.css +1 -0
- package/dist/web/assets/ui-DcmhYyYp.js +13 -0
- package/dist/web/assets/ui-DxEDgbIx.css +1 -0
- package/dist/web/assets/usePauseDot-BV60ZVy1.js +2 -0
- package/dist/web/assets/usePauseDot-NrSzMI1C.css +1 -0
- package/dist/web/assets/usePush-Dsm-TjlG.js +2 -0
- package/dist/web/assets/useRefChips-CbafNx59.js +68 -0
- package/dist/web/assets/useRestoreDot-D0B6W82H.js +2 -0
- package/dist/web/assets/view-worker-pyeVAhUT.js +2 -0
- package/dist/web/assets/workflowUi-QYsd3n9J.js +2 -0
- package/dist/web/favicon.png +0 -0
- package/dist/web/icon-192.png +0 -0
- package/dist/web/icon-512.png +0 -0
- package/dist/web/icon-maskable-512.png +0 -0
- package/dist/web/index.html +45 -0
- package/dist/web/manifest.webmanifest +23 -0
- package/dist/web/mascots/argus-attention.svg +7 -0
- package/dist/web/mascots/argus-ok.svg +7 -0
- package/dist/web/mascots/argus-outage.svg +7 -0
- package/dist/web/mascots/argus-running.svg +7 -0
- package/dist/web/mascots/bolt-attention.svg +7 -0
- package/dist/web/mascots/bolt-ok.svg +7 -0
- package/dist/web/mascots/bolt-outage.svg +7 -0
- package/dist/web/mascots/bolt-running.svg +7 -0
- package/dist/web/mascots/echo-attention.svg +7 -0
- package/dist/web/mascots/echo-ok.svg +7 -0
- package/dist/web/mascots/echo-outage.svg +7 -0
- package/dist/web/mascots/echo-running.svg +7 -0
- package/dist/web/mascots/lens-attention.svg +7 -0
- package/dist/web/mascots/lens-ok.svg +7 -0
- package/dist/web/mascots/lens-outage.svg +7 -0
- package/dist/web/mascots/lens-running.svg +7 -0
- package/dist/web/mascots/moss-attention.svg +7 -0
- package/dist/web/mascots/moss-ok.svg +7 -0
- package/dist/web/mascots/moss-outage.svg +7 -0
- package/dist/web/mascots/moss-running.svg +7 -0
- package/dist/web/mascots/nit-attention.svg +7 -0
- package/dist/web/mascots/nit-ok.svg +7 -0
- package/dist/web/mascots/nit-outage.svg +7 -0
- package/dist/web/mascots/nit-running.svg +7 -0
- package/dist/web/mascots/nova-attention.svg +7 -0
- package/dist/web/mascots/nova-ok.svg +7 -0
- package/dist/web/mascots/nova-outage.svg +7 -0
- package/dist/web/mascots/nova-running.svg +7 -0
- package/dist/web/mascots/penn-attention.svg +7 -0
- package/dist/web/mascots/penn-ok.svg +7 -0
- package/dist/web/mascots/penn-outage.svg +7 -0
- package/dist/web/mascots/penn-running.svg +7 -0
- package/dist/web/mascots/pip-attention.svg +7 -0
- package/dist/web/mascots/pip-ok.svg +7 -0
- package/dist/web/mascots/pip-outage.svg +7 -0
- package/dist/web/mascots/pip-running.svg +7 -0
- package/dist/web/mascots/ward-attention.svg +7 -0
- package/dist/web/mascots/ward-ok.svg +7 -0
- package/dist/web/mascots/ward-outage.svg +7 -0
- package/dist/web/mascots/ward-running.svg +7 -0
- package/dist/web/sw.js +98 -0
- package/dist/webhook-seen.js +53 -0
- package/dist/webhook-seen.js.map +1 -0
- package/dist/webhooks.js +625 -0
- package/dist/webhooks.js.map +1 -0
- package/dist/zip.js +63 -0
- package/dist/zip.js.map +1 -0
- package/docs/API-V2.md +532 -0
- package/docs/CHAT-EVAL.md +130 -0
- package/docs/CHAT-UI.md +416 -0
- package/docs/CONNECTIONS.md +89 -0
- package/docs/DATA-MODEL.md +518 -0
- package/docs/DEPLOY.md +577 -0
- package/docs/DOGFOOD-LEDGER.md +81 -0
- package/docs/GITHUB.md +148 -0
- package/docs/INTEGRATION-MODEL.md +200 -0
- package/docs/LEAD-DOT.md +553 -0
- package/docs/LIST-PATTERNS.md +52 -0
- package/docs/MULTI-REPO.md +766 -0
- package/docs/PARITY.md +276 -0
- package/docs/PR-REVIEW-QA.md +1266 -0
- package/docs/PR-REVIEW.md +357 -0
- package/docs/SANDBOX.md +320 -0
- package/docs/SETUP.md +776 -0
- package/docs/SLACK.md +80 -0
- package/docs/STAGES.md +619 -0
- package/docs/TRACE-MAP.md +89 -0
- package/docs/TRACKERS.md +225 -0
- package/docs/VOICE.md +36 -0
- package/docs/tinybee/00-README.md +45 -0
- package/docs/tinybee/01-PRD.md +216 -0
- package/docs/tinybee/02-architecture-pairing.md +199 -0
- package/docs/tinybee/03-local-agent.md +222 -0
- package/docs/tinybee/04-local-setup-ui.md +128 -0
- package/docs/tinybee/05-mobile-app-ux.md +199 -0
- package/docs/tinybee/06-environments-and-delivery.md +148 -0
- package/docs/tinybee/07-coding-agent-handoff.md +116 -0
- package/docs/tinybee/OPEN-QUESTIONS.md +26 -0
- package/docs/tinybee/PROGRESS.md +134 -0
- package/package.json +111 -4
package/docs/SETUP.md
ADDED
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
# Setup
|
|
2
|
+
|
|
3
|
+
Connect each service from the **Setup** page of the app. Every row on that page has a **Connect** button that opens a short flow: pick how localdost signs in, paste a key or press a sign-in button, press **Test**, then press **Connect**. The service starts working at once, with no restart. Each row links to its section of this guide, and the guide opens inside the app.
|
|
4
|
+
|
|
5
|
+
Environment variables still work, and they win. A service that an environment variable sets shows **Set by environment** on the Setup page, and the app shows it read-only. To manage it from the app instead, remove the variable and restart the server.
|
|
6
|
+
|
|
7
|
+
## Connect from the app
|
|
8
|
+
|
|
9
|
+
Open Setup from the side bar, or from the banner that appears while a service needs attention. The rows that need you come first.
|
|
10
|
+
|
|
11
|
+
| Button | What it does |
|
|
12
|
+
|---|---|
|
|
13
|
+
| **Connect** | Opens the flow for a service that is not connected yet |
|
|
14
|
+
| **Test** | Checks the values in the form, or the saved connection, against the real service and shows the result in the row. Test saves nothing |
|
|
15
|
+
| **Edit** | Opens the flow again with the saved choices filled in. Secrets show only their last 4 characters |
|
|
16
|
+
| **Disconnect** | Deletes the saved values. The service goes back to demo mode, or to the environment value when there is one |
|
|
17
|
+
| **Check again** | Runs a detection again, for example for Docker or a signed-in CLI |
|
|
18
|
+
|
|
19
|
+
Saved secrets are encrypted at rest in the data folder (`LOCALDOST_DATA_DIR`) with a key that localdost creates there on first use. The server never sends a saved secret back to the browser. Back up the whole data folder, because the saved values cannot be read without the key next to them.
|
|
20
|
+
|
|
21
|
+
The Setup page uses the same API as the rest of the app. On your own machine it works with no token. On any other host, set `LOCALDOST_TOKEN` first and enter the token on the Join screen of a new server or in Settings, so only token holders can connect services.
|
|
22
|
+
|
|
23
|
+
## Start a team server
|
|
24
|
+
|
|
25
|
+
A new server opens the setup flow at `#/welcome`. The flow is for the person who sets the server up. It has eleven steps, and a reload keeps you on the step you were on. While the flow is open, a **Getting started** link in the sidebar (and in the More menu on a phone) opens it again. **Close setup** ends it for now, and the Dots page then shows a Next steps card with what is left.
|
|
26
|
+
|
|
27
|
+
Start the server first. Set the token and the public URL, then build and start:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
npm ci && npm run build
|
|
31
|
+
LOCALDOST_TOKEN=$(openssl rand -hex 24) LOCALDOST_PUBLIC_URL=https://localdost.example.com npm start
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Step | What it does |
|
|
35
|
+
|---|---|
|
|
36
|
+
| Welcome | You choose **A team server** or **My laptop**. The team choice checks `LOCALDOST_TOKEN` and `LOCALDOST_PUBLIC_URL` and says what is missing. The check never blocks. **Try the demo** shows on a demo server only |
|
|
37
|
+
| Event delivery | The step says webhooks only after a signed delivery from GitHub reached this server (a ping counts, for up to 7 days). An https public URL and a webhook secret are not enough: a token has no webhook until you add one to each repo (Settings, Webhooks, Add webhook, payload URL `<public URL>/hooks/github`, content type `application/json`, the secret, and the events Pull requests, Pushes, Workflow runs, Deployment statuses, Issue comments, Issues), and a GitHub App made before the public URL was set has none either. Until a delivery arrives the dot polls GitHub every `POLL_GITHUB_SEC` seconds, and Check again on the step reads the server once you have added the hook. Polling does not see CI failures, preview deployments, or `/localdost review` comments |
|
|
38
|
+
| Model | A signed-in Claude or Codex login that localdost found is offered, never used before you say so. On a server with `LOCALDOST_TOKEN`, localdost picks none on its own. The Claude account email is hidden from anyone who is not on the server's machine. On a team server, use a team API key. **Continue** shows after any working choice |
|
|
39
|
+
| GitHub | Connect with a gh login, a GitHub App, or a token. On a first pass no repo is picked yet, so **Test access** here checks the connection. The Repos step then tests each picked repo and its issues endpoint by itself, and Create dot tests the code repos and the tracker repo together, and both name SAML SSO when GitHub asks for it. See [GitHub](#github) for the permissions |
|
|
40
|
+
| Repos | Each repo is an **App** repo (reviewed and tested), a **Tests** repo (the repo that holds the end-to-end suite, which the dot runs on PRs and merges of the app repos, see [Tests repo](#tests-repo)), or **Issues only** (read for tickets, not watched). An App repo can narrow its pull requests to some authors, and skip drafts and bots (the dot obeys it, see [PR scope](#pr-scope)), and it has a **Run tests for this repo** switch, on by default: turn it off for a back end with no Playwright suite and the dot reviews its pull requests and runs no tests (see [Run tests per repo](#run-tests-per-repo)). A Tests repo and an Issues only repo have no scope and no Run tests switch |
|
|
41
|
+
| Tracker | **GitHub Issues** comes first, with a picker for any repo the connection can read (it may hold no code). The repo starts as the Issues only repo you picked on the Repos step, else the first app repo. You choose the issue types (Feature, Task, Bug, Story), the labels an issue needs one of, and a milestone. The step counts the open issues that match, with the rule the dot uses. Linear and Jira are under **Other trackers** |
|
|
42
|
+
| Tests | Each app repo shows its own setup steps, test command, and QA base URL, filled in from the scan and the CI workflow. **Try once** runs them. A field saves when you leave it. A repo with Run tests off is left out and named. The dot runs one command for its app repos: the one of the first app repo that runs tests. A Tests repo has its own command and setup (see [Tests repo](#tests-repo)). Name the sign-in variables of the app under test in the dot dialog afterwards (**Test login variables**, see [Test login](SANDBOX.md#test-login)) |
|
|
43
|
+
| Slack | Connect Slack or skip it. Pick the channel (stored by its id, which is what routes mentions) and list the approvers by Slack member ID (`SLACK_ALLOWED_USERS`) |
|
|
44
|
+
| Rules | One list of every outward action with its level. The **shadow week** is on by default: for 7 days, PR comments and checks wait in the Inbox for your approval, then they post on their own |
|
|
45
|
+
| Create dot | The dot watches the repos with their roles, reads tickets from the tracker repo alone (an Issues only repo that is not the tracker repo is named as unread), and posts to the Slack channel. It marks an app repo with Run tests off as `(app, no tests)`. It refuses to be made with no app repo, says why on the button, and shows how it listens: `Listening: webhooks` or `Listening: polling every 60 s` |
|
|
46
|
+
| First review | **Review without posting** is the main button: the dot writes the review and its comment and check wait in the Inbox. **Post a comment and a check on PR #N** posts them, and shows only when no shadow week runs: during the shadow week both buttons leave the comment and the check in the Inbox, so the step says so with the end date |
|
|
47
|
+
|
|
48
|
+
While the shadow week runs, Settings shows it above the Rules of the dot with its end date and an **End shadow week** button, and the dot page lists it under Sources. Saving the dot dialog does not end it: only a rule you change by hand leaves the week.
|
|
49
|
+
|
|
50
|
+
In sandbox mode (`LOCALDOST_SANDBOX=1`) jobs do not run, so the Repos and First review steps say so and offer no scan or review.
|
|
51
|
+
|
|
52
|
+
After the flow, the Next steps card has a **Connect True Platform** row. Enter the https address of your Katalon MCP server. localdost adds it as an HTTP connection from the `katalon-true-platform` preset. If the server needs a token, put the request headers as JSON in `KATALON_MCP_HEADERS` on the localdost server. More: [CONNECTIONS.md](CONNECTIONS.md#presets).
|
|
53
|
+
|
|
54
|
+
### The shadow week
|
|
55
|
+
|
|
56
|
+
A dot made by the flow has `shadowWeek` on it: the end date, the rules it holds (`pr_comment` and `check_run`), and the levels they go back to. A server job runs at boot and every 5 minutes, and a check inside the rules gate reads an ended week as its old level at once, so a stopped timer cannot keep a dot silent. A rule you set by hand during the week leaves the week and keeps your level. Only a rule that acts on its own is held back. A rule already at Ask first, When asked, or Hand off stays as it is. To start or end a week yourself, send `shadowWeekDays` (1 to 30, or 0 to end it now) to `PATCH /api/dots/:id`.
|
|
57
|
+
|
|
58
|
+
## Settings files
|
|
59
|
+
|
|
60
|
+
You can also keep settings in files. `localdost start` loads `.env.localdost` from the repo folder, and a variable already set in your shell wins over the file. `localdost init` writes two files in the target repo:
|
|
61
|
+
|
|
62
|
+
- `.env.localdost` holds settings and references, never secrets. `localdost init` adds it to `.gitignore`.
|
|
63
|
+
- `.localdost/config.json` holds the detected repo, tracker, test command, port, model, and dot id. It has no secrets and is safe to commit.
|
|
64
|
+
|
|
65
|
+
A value in `.env.localdost` can be a literal or a reference:
|
|
66
|
+
|
|
67
|
+
| Form | Example | Result |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Literal | `PORT=4319` | The value as written |
|
|
70
|
+
| `${env:NAME}` | `LINEAR_API_KEY=${env:LINEAR_API_KEY}` | `NAME` from the shell that runs `localdost start` |
|
|
71
|
+
| `${cmd:...}` | `GITHUB_TOKEN=${cmd:gh auth token}` | The output of one read-only command, run without a shell |
|
|
72
|
+
|
|
73
|
+
A `${cmd:...}` reference runs only `gh auth token`, `op read`, `security find-generic-password`, `security find-internet-password`, or `pass show`. Any other command stays unresolved, and `localdost start` prints a warning for it. A relative `LOCALDOST_DATA_DIR` resolves against the repo folder.
|
|
74
|
+
|
|
75
|
+
Docker Compose reads `.env` next to `compose.yml` instead. `.env.example` in the localdost folder lists the common variables. The Setup page, `GET /api/setup`, and `localdost doctor` run the same readiness checks.
|
|
76
|
+
|
|
77
|
+
## Server
|
|
78
|
+
|
|
79
|
+
Server settings are read once at boot, so they have no Connect flow. Set them in the environment.
|
|
80
|
+
|
|
81
|
+
| Variable | Default | Purpose |
|
|
82
|
+
|---|---|---|
|
|
83
|
+
| `PORT` | `4319` | Listen port |
|
|
84
|
+
| `HOST` | `127.0.0.1` without a token, `0.0.0.0` with one | Listen address. A non-loopback `HOST` without `LOCALDOST_TOKEN` stops the server at boot |
|
|
85
|
+
| `LOCALDOST_TOKEN` | unset | Bearer token for `/api/*` and `/artifacts/*`, sent only in the `Authorization` header. Without it, `/api` answers only requests whose `Host` header is a loopback name, and webhooks answer only with their secret set (see `LOCALDOST_ALLOW_UNSIGNED_WEBHOOKS`) |
|
|
86
|
+
| `LOCALDOST_ALLOW_UNSIGNED_WEBHOOKS` | off | `1` lets `/hooks/*` accept events from a source whose webhook secret is unset, as older versions did. The server logs a warning at start. For local demos only: anyone who can reach the URL can queue jobs. `LOCALDOST_DEMO` does not turn it on |
|
|
87
|
+
| `LOCALDOST_PUBLIC_URL` | unset | HTTPS origin used in links in comments, pushes, and Slack. See [Public URL](#public-url) |
|
|
88
|
+
| `LOCALDOST_SERVER_NAME` | unset | The name the Join screen shows, for example `Agentic pod QA`. Without it the screen shows the host name. See [Join a team server](#join-a-team-server) |
|
|
89
|
+
| `LOCALDOST_DEMO` | off | `1` seeds the Checkout dot and starts the sample shop |
|
|
90
|
+
| `LOCALDOST_SAMPLE_PORT` | `4320` | Port of the demo sample shop |
|
|
91
|
+
| `LOCALDOST_SANDBOX` | off | `1` serves the app and the API over the existing data and lets nothing leave. See [Sandbox mode](#sandbox-mode) |
|
|
92
|
+
| `LOCALDOST_DATA_DIR` | `data/` in the localdost folder, `.localdost/data` under `localdost start` | Database, push keys, saved connections and their key, artifacts, checkouts, eval candidates |
|
|
93
|
+
| `LOCALDOST_DB` | `<LOCALDOST_DATA_DIR>/localdost.db` | SQLite file. `:memory:` keeps it in memory |
|
|
94
|
+
| `LOCALDOST_WEB_DIR` | `dist/web` | Built PWA folder |
|
|
95
|
+
| `LOCALDOST_COMMIT_SHA` | git, else unset | Commit this build came from, for `GET /api/health`. Set it in an image or host that has no `.git` folder. See [Version](#version) |
|
|
96
|
+
| `LOCALDOST_DISK_MIN_FREE_GB` | `2` | Free space the data folder must keep. Under it, new test runs wait. `0` turns the check off. See [Disk space](#disk-space) |
|
|
97
|
+
| `LOCALDOST_ARTIFACTS_MAX_GB` | `2` | Most artifact space one dot may use. Over it, the oldest runs lose screenshots, videos, and traces. `0` turns the cap off. See [Disk space](#disk-space) |
|
|
98
|
+
| `LOG_LEVEL` | `info` | Fastify log level |
|
|
99
|
+
| `LOCALDOST_USE_DIST` | unset | `1` makes `bin/localdost.js` run the built `dist/` code even when `src/` exists |
|
|
100
|
+
|
|
101
|
+
Flag variables (`LOCALDOST_DEMO`, `LOCALDOST_SANDBOX`, `LOCALDOST_POLL`) accept `1`, `true`, or `yes`.
|
|
102
|
+
|
|
103
|
+
## Join a team server
|
|
104
|
+
|
|
105
|
+
A team server is one localdost that several people use. The admin sets it up once and invites each person. A teammate needs no admin token and no setup of their own.
|
|
106
|
+
|
|
107
|
+
### Invite a teammate
|
|
108
|
+
|
|
109
|
+
1. Open **Settings** and press **Invite a teammate**, or press **Invite** on the Invite a teammate row of the Next steps card on the Dots page.
|
|
110
|
+
2. Pick how long the code lasts: 1 hour, 1 day, or 7 days, and the role the teammate joins as: **Member** or **Editor** (see [Roles](#roles)). Press **Make invite code**.
|
|
111
|
+
3. The sheet shows the code with its role, a link, and a message for Slack. Press **Copy message** and send it to one person.
|
|
112
|
+
|
|
113
|
+
A code works once, so make one per person: **Make another code** adds one, and **Cancel open invites** cancels every code nobody has used. A code is stored as a hash, never as text, and it survives a restart.
|
|
114
|
+
|
|
115
|
+
The expiry in the Slack message carries the time zone of your browser, for example `GMT+7`, so a teammate in another zone can read it. After a code is made, the Expires in and Joins as choices are for the next code, and the code on screen keeps the ones it was made with.
|
|
116
|
+
|
|
117
|
+
The link uses the address the sheet shows. The sheet leads with the address of the page you have open when a teammate can use it, then Tailscale, the Public URL, and the same Wi-Fi. A page that is open on localhost gives a teammate nothing to open, so the sheet says so and points to [Public URL](#public-url). Set `LOCALDOST_SERVER_NAME` to give the server a name on the Join screen.
|
|
118
|
+
|
|
119
|
+
### Join as a teammate
|
|
120
|
+
|
|
121
|
+
1. Open the server link. A browser with no token shows a Join screen with the server name, not an error. On a server with no dot yet, the same screen also asks **Admin? Enter the server token**: paste `LOCALDOST_TOKEN` (the service Variables on Railway, or the `.env` file next to `compose.yml`), and **Save and open Getting started** checks the token and opens the setup flow. A wrong token says so at the field and keeps nothing.
|
|
122
|
+
2. Type your name and the code. The server keeps the device under your name, and the name goes next to every draft you approve or reject. A name another device already uses is refused: add a last name or the device, for example `Cy Nguyen (laptop)`.
|
|
123
|
+
3. Pick your updates: everything, only what needs you, or off, with optional quiet hours. They apply to your device only. Settings keeps them afterwards.
|
|
124
|
+
4. Read the three cards: Inbox, a run page, and `@dot` in Slack. **Open Inbox** ends the tour.
|
|
125
|
+
|
|
126
|
+
A wrong or old code gets "Ask your admin for a new one". One client may send 5 wrong codes in 10 minutes before it is locked out, and 20 wrong codes from anyone cancel the open phone code. Invites are not cancelled that way: they stay until you cancel them or they expire.
|
|
127
|
+
|
|
128
|
+
A teammate's device is a paired device with a role. An admin revokes it, or changes its role, under **Setup**, then **Phone**, in the Paired devices list.
|
|
129
|
+
|
|
130
|
+
### Roles
|
|
131
|
+
|
|
132
|
+
The invite gives the device a role. The server holds the role on every call, and the app shows a device only the controls its role may use. A refused action says "Ask your localdost admin".
|
|
133
|
+
|
|
134
|
+
| | Member | Editor | Admin token |
|
|
135
|
+
|---|---|---|---|
|
|
136
|
+
| Approve and reject drafts, chat, rerun tests, ask for a review, record manual results | yes | yes | yes |
|
|
137
|
+
| Pause and resume a dot | yes | yes | yes |
|
|
138
|
+
| Dot settings and rules, a dot's memory, Try it on the saved test command, card actions (Comment, Change status) | no | yes | yes |
|
|
139
|
+
| The test command, setup steps, test login variables, copy files, Docker image, runner mode, folders of a dot or a test repo, and the deploy wait | no | no | yes |
|
|
140
|
+
| The repositories of a dot and their roles, the branch of each, the repos a test repo exercises and its ref and framework, and the test branches | no | no | yes |
|
|
141
|
+
| Send to True Platform: the switch, the address, the project, and the connection check | no | no | yes |
|
|
142
|
+
| Setup, connections, the model, invites, pairing, a new dot, archive, restore, and delete a dot | no | no | yes |
|
|
143
|
+
|
|
144
|
+
What runs on the server as its user stays with the admin: an editor sees the test command, setup steps, test login variables, copy files, Docker image, runner mode, and working folder read-only in the dot form, with "Ask your localdost admin to change test commands.", and the server answers a change to them with a 403 and the same sentence. The deploy wait ([Waiting for the deploy](#waiting-for-the-deploy)) is read-only for an editor the same way, with "Ask your localdost admin to change the deploy wait." Try it runs the saved command for an editor, never a new one. Which code a run checks out stays with the admin too: an editor sees the repository, its default branch, and the test branches read-only in the dot form, with "Ask your localdost admin to change repositories.", and the server answers a change to them with a 403 and that sentence. Try it clones only the dot's own repo and branch for an editor. Path filters, the PR scope, and the Run tests switch stay open to an editor. A phone paired before roles existed could archive and restore a dot; it is a member now and cannot, and neither can an editor. The dot's chat follows the same line: a member asks and reruns, and a request to remember something needs an editor.
|
|
145
|
+
|
|
146
|
+
No device can take the name `admin`: approvals made with the admin token are recorded under that name, and a teammate with the same one could not be told apart. A device paired before roles existed, a device paired with a phone code, and the Tailscale owner are members. Change a role at any time in the Paired devices list; the device's next request uses it.
|
|
147
|
+
|
|
148
|
+
### Pair this computer under your name
|
|
149
|
+
|
|
150
|
+
A decision made with the admin token is recorded as `admin`, and no device may use that name. In **Settings**, under This device, press **Pair this computer as <name>** to make a device in your name (an editor, not counted as a teammate). The browser uses that key from then on, so each approval carries your name. The admin token stays in the browser: **Use the admin token on this computer** switches back for Setup, connections, and removing a dot, and **Use <name> for approvals** switches again.
|
|
151
|
+
|
|
152
|
+
### Who approved a draft
|
|
153
|
+
|
|
154
|
+
Every approval keeps `decidedBy` when a named person decided it: a paired device by its name, the Tailscale owner by login, or a Slack user as `Slack <name>`, the name Slack sends with the button press (`Slack <user id>` when it sends none). The Inbox card, the Recent decisions rows, and the run step show it. The shared admin token is recorded as `admin`. A local browser on a server with no token names no one, so a draft it decides shows no name. A card action (Comment, Change status) keeps who asked on the run step (`Asked on a card by <name>`) and on the draft (`meta.askedByName`).
|
|
155
|
+
|
|
156
|
+
## Version
|
|
157
|
+
|
|
158
|
+
localdost versions follow [Semantic Versioning](https://semver.org/) and stay at major 0 until the public launch. `package.json` holds the version, and [CHANGELOG.md](https://github.com/duonghaijoe/localdost/blob/main/CHANGELOG.md) lists what changed in each one.
|
|
159
|
+
|
|
160
|
+
- The app shows **localdost X.Y.Z** under Settings, About, and as a tooltip on the status line at the bottom of the sidebar.
|
|
161
|
+
- `localdost --version` prints the same words.
|
|
162
|
+
- `GET /api/health` returns `version` and `commit`, and `timeZone` and `tzOffsetMin`, the zone and offset of the server clock that the nightly test run follows. The commit comes from `LOCALDOST_COMMIT_SHA`, then from `RAILWAY_GIT_COMMIT_SHA` or `SOURCE_COMMIT`, then from `git rev-parse HEAD` when the folder is a git checkout. It is `null` when none of them knows.
|
|
163
|
+
|
|
164
|
+
## Sandbox mode
|
|
165
|
+
|
|
166
|
+
`LOCALDOST_SANDBOX=1` starts a server that only serves the app and the API over the data it already has. Use it to look at a copy of real data without any chance of a comment, a ticket, a Slack post, or a phone alert going out. Not to be confused with the [Docker runner](#sandbox) below, which runs tests in containers.
|
|
167
|
+
|
|
168
|
+
What stays off:
|
|
169
|
+
|
|
170
|
+
- The job queue does not start or claim jobs. Queued jobs stay queued, and a job that was running when the last server stopped is left as it was.
|
|
171
|
+
- Pollers, HTTP and browser probes, status-page checks, and digests do not run.
|
|
172
|
+
- Webhooks answer `503` with a plain message (`/hooks/github`, `/hooks/jira`, `/hooks/linear`). Nothing is checked or queued.
|
|
173
|
+
- Slack does not connect.
|
|
174
|
+
|
|
175
|
+
What is refused: every outward write. A GitHub comment, check run, or commit status, a pull request branch, a GitHub, Linear, or Jira issue or comment, a Slack post, a phone push or ntfy message, and an MCP tool that changes data. Each refusal logs one line, `sandbox: refused <kind> to <target>`, and sends nothing over the network. A draft you approve in the Inbox stays there with that reason.
|
|
176
|
+
|
|
177
|
+
What still works: the app, the API, chat, and model calls, so a chat with a dot still gets a real answer. Reads from GitHub, Linear, and Jira work as before.
|
|
178
|
+
|
|
179
|
+
The app shows the banner **Sandbox: nothing leaves this server.** at the top of every page. `GET /api/health` returns `"sandbox": true` and the same text as `sandboxBanner`. Restart the server without the flag to leave sandbox mode.
|
|
180
|
+
|
|
181
|
+
## Disk space
|
|
182
|
+
|
|
183
|
+
A full disk used to stop the server with "database or disk is full". Two settings keep it up. Both count in GB, where 1 GB is 1,000,000,000 bytes, the way macOS counts. A value of `0` turns that setting off. A blank or invalid value uses the default.
|
|
184
|
+
|
|
185
|
+
| Variable | Default | Purpose |
|
|
186
|
+
|---|---|---|
|
|
187
|
+
| `LOCALDOST_DISK_MIN_FREE_GB` | `2` | Free space the data folder must keep |
|
|
188
|
+
| `LOCALDOST_ARTIFACTS_MAX_GB` | `2` | Most artifact space one dot may use |
|
|
189
|
+
|
|
190
|
+
**Low free space.** localdost reads the free space under `LOCALDOST_DATA_DIR` (and the folder of `LOCALDOST_DB` when it is elsewhere) at start and every 3 minutes. Under `LOCALDOST_DISK_MIN_FREE_GB`, the dot takes no new test runs: PR checks, post-merge runs, CI failure reviews, reruns, and repo scans stay queued. Outage and recovery alerts, ticket reviews, and digests still run. The app and the API keep answering. The app shows a **Disk almost full** banner with a link to this section, and one push goes out for each time the space runs short. The queued jobs start by themselves at the first check that finds enough space again. A run that is already going is not stopped.
|
|
191
|
+
|
|
192
|
+
**A write that finds the disk full.** If SQLite reports "database or disk is full", localdost logs the error, keeps the server running, answers the request with status `507`, pauses new test runs the same way, and shows a **Disk full** banner. The pause ends after a minute with no failed write, once the free space is back over the threshold. A job that could not save its result stays as it was. The next start marks its run interrupted, and Retry runs it again.
|
|
193
|
+
|
|
194
|
+
**Artifact cap.** Run artifacts live in `<LOCALDOST_DATA_DIR>/artifacts/<run id>/`. When a dot's runs use more than `LOCALDOST_ARTIFACTS_MAX_GB`, localdost deletes files from its oldest runs first. Inside a run it deletes the largest screenshot, video, or trace first, and it stops as soon as the dot is under the cap. It never deletes a run record, a log, or a JSON report, and it skips a run that is still going, so a dot can stay a little over the cap when only those are left. localdost checks the cap when a run ends and on each disk check, and it logs what it removed.
|
|
195
|
+
|
|
196
|
+
**Free space by hand.** Delete old folders under `artifacts/` and `checkouts/` in the data folder, remove Docker images you no longer use, or empty the Trash. Then wait for the next check (up to 3 minutes) or restart the server.
|
|
197
|
+
|
|
198
|
+
## Model
|
|
199
|
+
|
|
200
|
+
The model writes verdicts, ticket scores, action reviews, PR reviews, and chat replies. It can run on your Claude subscription, your ChatGPT plan through the Codex CLI, or an Anthropic API key.
|
|
201
|
+
|
|
202
|
+
**In the app.** On the Model row, press **Connect** and pick one:
|
|
203
|
+
|
|
204
|
+
- **Claude subscription** uses your Claude Pro or Max plan, with no API billing. When the server finds a signed-in Claude CLI, the flow says so and **Use it** connects in one click. Otherwise run `claude setup-token` on any machine with the Claude CLI, sign in with your Claude account in the browser it opens, and paste the token it prints (it starts with `sk-ant-oat` and lasts a year). A token works on a server where nobody can sign in to the CLI.
|
|
205
|
+
- **Codex subscription** uses your ChatGPT plan through the Codex CLI, with no API billing. Run `codex login` on the server first. When the server finds a signed-in Codex CLI, **Use it** connects in one click and sets `LOCALDOST_LLM=codex-cli`. **Test** runs `codex login status`, which spends no usage.
|
|
206
|
+
- **API key** uses the Anthropic API and bills per use. Create a key in the Anthropic Console, paste it (it starts with `sk-ant-`), press **Test**, then **Connect**. This fits a shared server.
|
|
207
|
+
|
|
208
|
+
**Switch models.** The Model sheet and Settings, Model, list every way this server can reach a model: a signed-in Claude CLI, a signed-in Codex CLI, a saved Claude token, and a saved Anthropic key. The one in use is marked. **Switch** runs a test round trip first, then saves the choice; the dots pick it up without a restart. Switching to the signed-in Claude CLI removes a saved Claude token.
|
|
209
|
+
|
|
210
|
+
**When a model hits a quota or spend cap.** A model call can fail because the plan ran out, the workspace owner set a spend cap, the provider rate limits you, or the sign-in expired. One classifier reads the error of every backend (Codex CLI, Claude CLI, Anthropic API, and the PR review passes) and sorts it into `quota`, `spend_cap`, `rate_limit`, `auth_expired`, or `other`. For the first four:
|
|
211
|
+
|
|
212
|
+
- The model is marked unavailable with the reason and a time to check again (the provider's retry-after when it gives one, else 1 minute for a rate limit, 5 for an expired sign-in, 15 for a quota, 30 for a spend cap; each failed check doubles the wait, up to an hour). The Model row in Setup and the entry in the model list read `Model unavailable: <reason>`.
|
|
213
|
+
- The call moves to the next model on the list that is signed in or has a saved key, in list order, and the run gets a "Model fallback" step. One push says which model took over. The mock model is never a fallback. A subscription (Claude CLI, Codex CLI) never falls back to a paid per-use API key unless you turn on **Allow paid fallback** in Settings > Model (`LOCALDOST_ALLOW_PAID_FALLBACK=1` also works). It is off by default. With it off, a capped subscription and only a paid key left ends the run as "Model unavailable", and the step names the capped subscription and says that paid fallback is off. A server with `LOCALDOST_TOKEN` is shared, so it never falls back to a Claude or Codex CLI login of its machine either, unless an admin chose that login: Keep this login on the Model step and the model switcher both save the choice, and a login localdost picked on its own does not count. A saved Claude token is a team credential and stays a fallback. A model an admin picks for a stage of a dot is a choice too, so a stage set to a CLI login keeps using it. When nothing allowed is left, the run ends as "Model unavailable" and the step says that this is a team server and which login was not used.
|
|
214
|
+
- When the wait is over, the next call tries the first model again, and a check every 30 seconds does the same when no work is queued. A push says when it works again. A new key, token, or login saved in Setup clears the mark at once.
|
|
215
|
+
- When no model works, a run stops as interrupted with `Model unavailable: <reason>`, no verdict, and a Retry button. The dot's health does not change, since nothing was concluded about the product. Test results already collected stay on the run. Chat answers `Model unavailable: <reason>`. One high-urgency push says that no model works, at most once an hour.
|
|
216
|
+
|
|
217
|
+
**First start.** When no model is chosen (nothing saved, no `LOCALDOST_LLM`, no key or token in the environment), localdost uses the first signed-in subscription it finds, the Codex CLI first, then the Claude CLI, instead of the mock model. Demo mode keeps the mock model. A server with `LOCALDOST_TOKEN` is shared, and a CLI login on it belongs to one person, so it picks nothing: the Model step of the setup flow lists the logins it found and waits for you. On a server without a token, the Model step asks whether to keep the login localdost picked. The server hides the Claude account email from every caller that is not on its own machine (a loopback socket and a loopback `Host` header).
|
|
218
|
+
|
|
219
|
+
**With environment variables.** `LOCALDOST_LLM` picks the backend:
|
|
220
|
+
|
|
221
|
+
| Value | Backend |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `claude-cli` | The local `claude` CLI in print mode, signed in with your own Claude login or `CLAUDE_CODE_OAUTH_TOKEN`. No API key needed |
|
|
224
|
+
| `codex-cli` | The local `codex` CLI (`codex exec`), signed in with your own ChatGPT login. No API key needed |
|
|
225
|
+
| `anthropic` | The Anthropic API with `ANTHROPIC_API_KEY` |
|
|
226
|
+
| `mock` | Deterministic canned verdicts, used by demo mode and the e2e suite |
|
|
227
|
+
| unset | `anthropic` when `ANTHROPIC_API_KEY` is set, else `mock` |
|
|
228
|
+
|
|
229
|
+
| Variable | Default | Purpose |
|
|
230
|
+
|---|---|---|
|
|
231
|
+
| `LOCALDOST_LLM` | unset | Backend, see above |
|
|
232
|
+
| `ANTHROPIC_API_KEY` | unset | Anthropic API key. Analysis uses `claude-sonnet-5-5`, scores and action reviews use `claude-haiku-4-5-20251001` |
|
|
233
|
+
| `CLAUDE_CODE_OAUTH_TOKEN` | unset | Subscription token from `claude setup-token`, used by the `claude-cli` backend |
|
|
234
|
+
| `LOCALDOST_MODEL`, `LOCALDOST_FAST_MODEL` | see `ANTHROPIC_API_KEY` | Main and fast model for either backend. The Setup page saves these names |
|
|
235
|
+
| `LOCALDOST_CLAUDE_BIN` | `claude` | Path to the Claude CLI when it is not on `PATH` |
|
|
236
|
+
| `LOCALDOST_CLAUDE_MODEL` | `sonnet` | CLI model for analysis and chat, 180 s timeout. Wins over `LOCALDOST_MODEL` |
|
|
237
|
+
| `LOCALDOST_CLAUDE_FAST_MODEL` | `haiku` | CLI model for scores and action reviews, 60 s timeout. Wins over `LOCALDOST_FAST_MODEL` |
|
|
238
|
+
| `LOCALDOST_REVIEW_MODEL` | `LOCALDOST_CLAUDE_MODEL` on the CLI, `claude-sonnet-5-5` on the API | Model for the PR review reviewer pass |
|
|
239
|
+
| `LOCALDOST_VERIFY_MODEL` | the review model | Model for the PR review verifier pass |
|
|
240
|
+
| `LOCALDOST_CLAUDE_REVIEW_TIMEOUT_SEC` | `300` | Timeout of one PR review call on the CLI |
|
|
241
|
+
| `LOCALDOST_CODEX_BIN` | `codex` | Path to the Codex CLI when it is not on `PATH` |
|
|
242
|
+
| `LOCALDOST_CODEX_MODEL` | the CLI default | Codex model for analysis, chat, and the PR review |
|
|
243
|
+
| `LOCALDOST_CODEX_FAST_MODEL` | `LOCALDOST_CODEX_MODEL` | Codex model for scores and action reviews |
|
|
244
|
+
| `LOCALDOST_CODEX_EFFORT` | the CLI default | Reasoning effort passed to `codex exec` |
|
|
245
|
+
| `LOCALDOST_CODEX_TIMEOUT_SEC` | 240 analysis, 180 chat, 120 scores, 300 PR review | One timeout for every Codex call |
|
|
246
|
+
| `LOCALDOST_CODEX_USER_CONFIG` | unset | `1` loads your `~/.codex/config.toml`. Off by default, see below |
|
|
247
|
+
|
|
248
|
+
In `claude-cli` mode each call runs `claude -p` in a new empty temp folder with built-in tools off, no settings files, and no saved session. Only a chat turn attaches an MCP server: the localdost chat tools (`src/agent/chat-mcp.ts`), allowed by exact name with `--allowedTools`, and no other MCP server. The prompt goes in over stdin, so ticket and diff text never shows in the process list. The PR review starts the CLI with an allowlisted environment. Other calls remove `GITHUB_*`, `LINEAR_*`, `JIRA_*`, `NTFY_*`, `VAPID_*`, webhook secrets, and `LOCALDOST_TOKEN` from the CLI's environment. A reply that fails to parse is retried once. After that the dot falls back to the canned verdict with confidence capped at 0.3 and a note in the summary. An analysis on `sonnet` takes about 90 seconds.
|
|
249
|
+
|
|
250
|
+
In `codex-cli` mode each call runs `codex exec --json --sandbox read-only --ephemeral` in a new empty temp folder. The CLI runs in its own process group, so a timeout stops any process it started too. User MCP servers are off, and shell, browser, computer use, apps, plugins, hooks, and memories are turned off with `-c features.<name>=false`. A chat turn adds one MCP server through `-c mcp_servers.localdost=...`: the localdost chat tools, limited to their names with `enabled_tools`. Codex runs them without asking, because the tools check the dot rules themselves. The prompt goes in over stdin, and the answer comes back through `--output-last-message`. The child environment is an allowlist (`HOME`, `PATH`, `CODEX_HOME`, proxy and CA variables), so `OPENAI_API_KEY`, `ANTHROPIC_*`, and tracker credentials stay out. Your `~/.codex/config.toml` is ignored by default: a model it pins can be one a ChatGPT login rejects, and its plugin MCP servers and hooks would still start. Analysis, scores, and action reviews expect JSON. A reply that fails to parse is sent back once with the problem. After that the run falls back to the rule-based verdict and adds a "Model fallback" step with the reason. An action review without a model decision always goes to a person. Each call costs about 17K input tokens, mostly skill descriptions the CLI loads.
|
|
251
|
+
|
|
252
|
+
Chat on both CLI backends calls the localdost tools for real (KAT-133). The CLI starts `chat-mcp.ts` as a stdio MCP server for the turn. The server holds no data: it forwards each call over a loopback HTTP bridge (`src/agent/chat-bridge.ts`, a random token per turn) to the running localdost server, which runs the same chat tool the Anthropic backend uses. Read-only tools answer directly, actions go through `gate()`, and only a tool the model calls shows as a card. The answer streams: `claude -p --output-format stream-json --include-partial-messages` and `codex exec --json` events reach the chat as they arrive. Chat runs at low reasoning effort unless `LOCALDOST_CODEX_EFFORT` is set.
|
|
253
|
+
|
|
254
|
+
A Claude CLI or Codex CLI login lives on one machine. On a server, use a subscription token or an API key. The boot log names the active backend.
|
|
255
|
+
|
|
256
|
+
## GitHub
|
|
257
|
+
|
|
258
|
+
**In the app.** On the GitHub row, press **Connect** and pick one:
|
|
259
|
+
|
|
260
|
+
- **GitHub CLI login** reuses `gh` on the server. When `gh` is signed in, **Use GitHub CLI login** connects, and localdost reads the token from `gh` each time it starts. This is the fastest way on your own machine.
|
|
261
|
+
- **GitHub App** creates an App for you. Enter an organization, or leave it empty for your personal account, and press **Create GitHub App**. GitHub opens with the permissions filled in. Create the App, install it on your repos, and GitHub sends you back to Setup connected. This fits a team server.
|
|
262
|
+
- **Personal access token** takes a fine-grained token that you paste. It starts with `github_pat_` or `ghp_`.
|
|
263
|
+
|
|
264
|
+
After you connect, pick the repos your dots watch. Permissions, webhooks, and polling: [GITHUB.md](GITHUB.md).
|
|
265
|
+
|
|
266
|
+
**What an organization needs.** An owner has to approve a GitHub App, or a fine-grained token, before it can read the organization's repos. When the organization enforces SAML SSO, authorize the token for it. Until then GitHub answers as if the repo did not exist, and **Test access** in the setup flow names SSO. Set the public URL before you press **Create GitHub App**: the App gets a webhook only when an https public URL is already set.
|
|
267
|
+
|
|
268
|
+
**Test.** The Test button on the GitHub row checks who the credentials are, then reads every repo in the workspace and its issues endpoint with them. It fails and names the repo when one cannot be read, and it names SAML SSO when GitHub asks for it. Connect runs the identity check only. **Test access** in the setup flow does the same for the repos you picked there.
|
|
269
|
+
|
|
270
|
+
**Token permissions.** A fine-grained token needs Contents read, Metadata read, Pull requests read and write, Commit statuses read and write, Issues read and write, Actions read, and Deployments read.
|
|
271
|
+
|
|
272
|
+
**With environment variables.**
|
|
273
|
+
|
|
274
|
+
| Variable | Purpose |
|
|
275
|
+
|---|---|
|
|
276
|
+
| `GITHUB_APP_ID`, `GITHUB_PRIVATE_KEY` | GitHub App mode. The key is the PEM text (with `\n` escapes allowed) or a path to the `.pem` file. The installation is looked up per repo |
|
|
277
|
+
| `GITHUB_TOKEN` | PAT mode, used when the App variables are unset |
|
|
278
|
+
| `GITHUB_WEBHOOK_SECRET` | HMAC secret for `/hooks/github`. Unset: the route answers 503 and queues nothing |
|
|
279
|
+
| `GITHUB_API_URL` | API base, default `https://api.github.com`. Set it for GitHub Enterprise Server |
|
|
280
|
+
| `GITHUB_SERVER_URL` | Web and clone base, default `https://github.com` |
|
|
281
|
+
| `LOCALDOST_MIRROR_CHECKOUT` | `1` takes PR review checkouts as worktrees of one partial mirror per repo in `LOCALDOST_DATA_DIR/mirrors`, in place of a fresh clone per review. Off by default. Repo scans (`localdost scan`, the `repo_scan` job) always use the mirror |
|
|
282
|
+
|
|
283
|
+
With no connection, GitHub runs in demo mode, and comments and check runs go to the outbox.
|
|
284
|
+
|
|
285
|
+
## Jira
|
|
286
|
+
|
|
287
|
+
**In the app.** On the Jira row, press **Connect**. Enter your site (for example `acme.atlassian.net`), the email of your Atlassian account, and an API token from your Atlassian account settings. Press **Test**, then **Connect**, then pick the projects your dots watch. Jira has no poller, so it also needs a webhook and a [public URL](#public-url): [TRACKERS.md](TRACKERS.md#jira).
|
|
288
|
+
|
|
289
|
+
**With environment variables.**
|
|
290
|
+
|
|
291
|
+
| Variable | Purpose |
|
|
292
|
+
|---|---|
|
|
293
|
+
| `JIRA_BASE_URL` | Site URL, for example `https://acme.atlassian.net`. A dot's `tracker.baseUrl` wins over it |
|
|
294
|
+
| `JIRA_EMAIL`, `JIRA_API_TOKEN` | Jira Cloud Basic auth |
|
|
295
|
+
| `JIRA_WEBHOOK_SECRET` | HMAC secret for `/hooks/jira`. Unset: the route answers 503 and queues nothing |
|
|
296
|
+
| `JIRA_AC_FIELD` | Custom field id that holds acceptance criteria, for example `customfield_10042`. Unset: the dot reads them from the description |
|
|
297
|
+
| `JIRA_BOT_ACCOUNT_ID` | Account id whose events the dot ignores, so its own comments never trigger a review |
|
|
298
|
+
|
|
299
|
+
## Linear
|
|
300
|
+
|
|
301
|
+
**In the app.** On the Linear row, press **Connect**. The flow links to the page in Linear where you create a personal API key (Settings, Security and access, New API key). Paste the key (it starts with `lin_api_`), press **Test**, then **Connect**, then pick the team and project your dots watch. Linear works with polling, so it needs no public URL: [TRACKERS.md](TRACKERS.md#linear).
|
|
302
|
+
|
|
303
|
+
**With environment variables.**
|
|
304
|
+
|
|
305
|
+
| Variable | Purpose |
|
|
306
|
+
|---|---|
|
|
307
|
+
| `LINEAR_API_KEY` | Personal API key for GraphQL reads, comments, and polling |
|
|
308
|
+
| `LINEAR_WEBHOOK_SECRET` | Signing secret for `/hooks/linear`. Unset: the route answers 503 and queues nothing |
|
|
309
|
+
| `LINEAR_API_URL` | GraphQL endpoint for polling, `localdost init`, and `localdost doctor`, default `https://api.linear.app/graphql` |
|
|
310
|
+
| `LINEAR_BOT_USER_ID` | User id whose issues the dot ignores |
|
|
311
|
+
|
|
312
|
+
## Poll mode
|
|
313
|
+
|
|
314
|
+
Poll mode lets a dot work without a public webhook URL. `localdost init` turns it on.
|
|
315
|
+
|
|
316
|
+
| Variable | Default | Purpose |
|
|
317
|
+
|---|---|---|
|
|
318
|
+
| `LOCALDOST_POLL` | off | Polls GitHub and Linear for every dot. One dot can opt in with `pollGithub: true` or `pollLinear: true` (a dot save accepts `pollGithub`; the setup flow sends it, `true` unless webhooks reach the server) |
|
|
319
|
+
| `POLL_GITHUB_SEC` | `60`, minimum 10 | GitHub poll interval |
|
|
320
|
+
| `POLL_LINEAR_SEC` | `60`, minimum 10 | Linear poll interval |
|
|
321
|
+
|
|
322
|
+
The first poll after boot records a cursor and queues nothing, except that a new dot takes the 5 most recently updated open PRs of its repos on its own first poll. After a restart of under an hour, polling catches up from the stored cursor. After a longer gap (KAT-363) GitHub polling takes the open PRs updated since the old cursor, and the issues of a GitHub tracker changed since, and Linear and Jira polling take the issues or tickets created or edited since, newest first, at most 5 per dot after the dot's scope, and each run starts with a "Caught up after a gap" step that names the gap; the commits of the gap start no post-merge run. A merge run that waits for a deploy looks at the deployments on GitHub every 30 s for a polling dot, see [Waiting for the deploy](#waiting-for-the-deploy). A dot made in the app polls GitHub when the server has no public URL or no webhook secret. The dot page and the dot list say which route a dot uses: "Listening: polling every 60 s", "Listening: webhooks", or "Not listening: ..." with what to fix. Jira has no poller. How each poller works: [GITHUB.md](GITHUB.md#polling) and [TRACKERS.md](TRACKERS.md#linear).
|
|
323
|
+
|
|
324
|
+
## Slack
|
|
325
|
+
|
|
326
|
+
**In the app.** On the Slack row, press **Connect** and follow the four steps in the flow:
|
|
327
|
+
|
|
328
|
+
1. Copy or download the app manifest. It turns on Socket Mode, so Slack needs no public URL.
|
|
329
|
+
2. Open Slack apps from the flow, create the app from the manifest, and install it to your workspace.
|
|
330
|
+
3. In OAuth and Permissions, copy the Bot User OAuth Token (`xoxb-`).
|
|
331
|
+
4. In Basic Information, App-Level Tokens, generate a token with the `connections:write` scope (`xapp-`).
|
|
332
|
+
|
|
333
|
+
Paste both tokens, press **Test**, then **Connect**, and pick the channel for notices. Allowlists and mentions: [SLACK.md](SLACK.md).
|
|
334
|
+
|
|
335
|
+
**With environment variables.**
|
|
336
|
+
|
|
337
|
+
| Variable | Purpose |
|
|
338
|
+
|---|---|
|
|
339
|
+
| `SLACK_APP_TOKEN` | App-level token (`xapp-`) for Socket Mode |
|
|
340
|
+
| `SLACK_BOT_TOKEN` | Bot token (`xoxb-`) |
|
|
341
|
+
| `SLACK_ALLOWED_TEAM` | Comma list of workspace ids that may ask the dots |
|
|
342
|
+
| `SLACK_ALLOWED_USERS` | Comma list of user ids that may ask the dots |
|
|
343
|
+
|
|
344
|
+
Slack starts only when both tokens are set.
|
|
345
|
+
|
|
346
|
+
## Push
|
|
347
|
+
|
|
348
|
+
**In the app.** Push needs no key. The server creates web push keys on first boot, and the Push row shows them as ready. Turn on alerts for each phone or browser in Settings. **Test** checks the push setup.
|
|
349
|
+
|
|
350
|
+
On iPhone, open the app from the Home Screen first: iOS delivers web push only to Home Screen apps. Open the app URL, tap Share, then Add to Home Screen, then open it from the Home Screen and turn on alerts in Settings. Web push needs HTTPS, except on `localhost`, so a phone also needs a [public URL](#public-url).
|
|
351
|
+
|
|
352
|
+
**With environment variables.**
|
|
353
|
+
|
|
354
|
+
| Variable | Default | Purpose |
|
|
355
|
+
|---|---|---|
|
|
356
|
+
| `VAPID_PUBLIC_KEY`, `VAPID_PRIVATE_KEY` | generated into `<LOCALDOST_DATA_DIR>/vapid.json` on first boot | Web push keys. Set them to keep the same keys across hosts |
|
|
357
|
+
| `VAPID_SUBJECT` | your `LOCALDOST_PUBLIC_URL` when it is https, else the project page | Contact the push services see. Apple refuses a `localhost` contact, so iPhone alerts fail with 403 |
|
|
358
|
+
| `NTFY_TOPIC` | unset | ntfy topic. Alerts go there too, with no app install |
|
|
359
|
+
| `NTFY_URL` | `https://ntfy.sh` | ntfy server |
|
|
360
|
+
| `NTFY_TOKEN` | unset | Bearer token for a protected ntfy server |
|
|
361
|
+
|
|
362
|
+
Push to the same key repeats at most once per 10 minutes.
|
|
363
|
+
|
|
364
|
+
## Public URL
|
|
365
|
+
|
|
366
|
+
The public URL is the HTTPS origin where people reach the app, for example `https://qa.example.com`. Links in PR comments, check runs, pushes, ntfy, and Slack use it. Without it those links have no run page. Push and the installable app also need HTTPS.
|
|
367
|
+
|
|
368
|
+
**In the app.** On the Public URL row, press **Connect** and pick one:
|
|
369
|
+
|
|
370
|
+
- **Quick tunnel** starts a Cloudflare quick tunnel with one click and shows the address it got. It needs `cloudflared` on the server and no Cloudflare account. The address changes each time the tunnel starts.
|
|
371
|
+
- **My own address** takes a URL that stays the same, such as your own domain, a named Cloudflare tunnel, or Tailscale Funnel. [DEPLOY.md](DEPLOY.md#your-machine-plus-cloudflared) shows how to set one up.
|
|
372
|
+
|
|
373
|
+
Set `LOCALDOST_TOKEN` before you start any tunnel. A tunnel makes the app reachable from the internet.
|
|
374
|
+
|
|
375
|
+
**With environment variables.** Set `LOCALDOST_PUBLIC_URL` to the origin. To run a quick tunnel yourself:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
cloudflared tunnel --url http://localhost:4319
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
On the same Wi-Fi, a server that listens on `0.0.0.0` prints a LAN address such as `http://192.168.1.20:4319` at boot. That works for the app, but push and the installable app still need HTTPS.
|
|
382
|
+
|
|
383
|
+
## Sandbox
|
|
384
|
+
|
|
385
|
+
Each dot picks a runner mode in `runner.mode`: `local`, `docker`, or `demo`. Docker mode needs a Docker daemon the server can reach.
|
|
386
|
+
|
|
387
|
+
**In the app.** The Docker runner row detects Docker by itself and has nothing to paste. When Docker is not reachable, start Docker Desktop or the Docker daemon on the server and press **Check again**. Runs still work in local or demo mode without it. Details: [SANDBOX.md](SANDBOX.md#docker).
|
|
388
|
+
|
|
389
|
+
**With environment variables.**
|
|
390
|
+
|
|
391
|
+
| Variable | Default | Purpose |
|
|
392
|
+
|---|---|---|
|
|
393
|
+
| `LOCALDOST_LIVE_FRAMES` | on for demo runs, off for local runs | `1` turns on browser frames for local runs, `0` turns them off. Docker runs never stream frames |
|
|
394
|
+
| `PLAYWRIGHT_VERSION` | `1.63.0` | Playwright image tag for Docker runs when the checkout does not declare `@playwright/test` |
|
|
395
|
+
| `LOCALDOST_RUN_LEASE_MS` | `60000`, minimum 5000 | How long a run lease lasts before a dead worker's run turns interrupted |
|
|
396
|
+
| `LOCALDOST_RUN_MAX_MS` | `3600000`, minimum 60000 | A run that never ends stops renewing its lease after this long |
|
|
397
|
+
| `STATUSPAGE_INTERVAL_SEC` | `180` | Statuspage poll interval |
|
|
398
|
+
|
|
399
|
+
`LOCALDOST_QUEUE_CONCURRENCY` sets how many jobs run at once (default 2, max 8). Each run gets its own ports and checkout, so a machine with spare CPU and memory can raise it.
|
|
400
|
+
|
|
401
|
+
`LOCALDOST_CDP_PORT` is set by the runner for each run. Do not set it yourself.
|
|
402
|
+
|
|
403
|
+
`LOCALDOST_RUN_PORT` and `LOCALDOST_RUN_PORT_2` are two free ports the runner gives each local run (not docker runs). Setup steps and the test command both see them. A suite that starts its own servers should listen on them, so two reviews can run at the same time. A value set in the test command wins.
|
|
404
|
+
|
|
405
|
+
## Voice
|
|
406
|
+
|
|
407
|
+
Calls with a dot use browser speech by default. An OpenAI key turns on realtime voice.
|
|
408
|
+
|
|
409
|
+
**In the app.** On the Voice row, press **Connect**, paste an OpenAI API key (it starts with `sk-`), press **Test**, then **Connect**. Realtime calls also need `LOCALDOST_TOKEN` on the server. Without it, calls keep using browser speech. Details: [VOICE.md](VOICE.md).
|
|
410
|
+
|
|
411
|
+
**With environment variables.**
|
|
412
|
+
|
|
413
|
+
| Variable | Default | Purpose |
|
|
414
|
+
|---|---|---|
|
|
415
|
+
| `OPENAI_API_KEY` | unset | Turns on OpenAI Realtime calls. The server mints a 10-minute client token per call and never sends the key to the browser |
|
|
416
|
+
| `OPENAI_REALTIME_MODEL` | `gpt-realtime-2.1` | Realtime model |
|
|
417
|
+
| `OPENAI_REALTIME_VOICE` | `marin` | Realtime voice |
|
|
418
|
+
|
|
419
|
+
## AI-Vision
|
|
420
|
+
|
|
421
|
+
AI-Vision runs natural-language browser cases next to Playwright, on an AI-Vision server (Veesan) or the open-source Midscene CLI. A dot turns it on with `runner.aivision`; see [SANDBOX.md](SANDBOX.md#ai-vision-cases).
|
|
422
|
+
|
|
423
|
+
**Found on this machine.** At start, when Setup opens, and every 3 minutes, localdost looks for a running server through `/aivision/health` at `AIVISION_PORT`, then `VEESAN_ENGINE_URL`, then `http://127.0.0.1:5880`. It looks for an installed copy at `AIVISION_CHECKOUT`, then `~/code/*aivision*`, then the folders next to the localdost checkout. A running server connects with nothing to type; the row reads Connected with its address and version. Detection asks for the web checks (`?platform=web`); a server that reports a failed check is shown with the check named and connects once it passes. When the copy is there but the server is down, the sheet offers **Start AI-Vision**: localdost runs `npx tsx apps/playground/demo/aivision-server.mts` in that folder as its own child process, with an allowlisted environment (paths, locale, proxies, and `VEESAN_*`, `AIVISION_*`, `MIDSCENE_*`, `PUPPETEER_*`, OpenAI and Codex variables), keeps its log, connects when it answers, and stops it when localdost stops. AI-Vision always runs on `gpt-5.6-luna`: localdost sets `VEESAN_MODEL_NAME` for the server it starts and for the AI-Vision CLI, whatever the dot's Codex model says. Without a model address it starts the server on the Codex CLI (`VEESAN_MODEL_BASE_URL=codex://`) and, on a Mac, with the installed Google Chrome. `LOCALDOST_AIVISION_DETECT=0` turns the search off.
|
|
424
|
+
|
|
425
|
+
**Dots.** While AI-Vision is connected, each dot that made no choice gets the AI-Vision MCP preset (from the found folder), and each dot with a repo and a base URL gets vision tests turned on. This happens once per dot: detach the preset or turn off **AI-Vision tests** in the dot's Test runner settings, and it stays off.
|
|
426
|
+
|
|
427
|
+
**A different server.** In the sheet, **Use a different server** takes an address, an access token when the server asks for sign-in, and the AI-Vision folder. A saved address wins over a found one. For a found server that asks for sign-in, the address is filled in, so only the token is left to type. The token goes only to the saved address, or to a server localdost started itself, never to another service that answers on a candidate port. **Test** lists the server's tests. A dot with the `server` backend and no `serverUrl` of its own uses this server.
|
|
428
|
+
|
|
429
|
+
**With environment variables.**
|
|
430
|
+
|
|
431
|
+
| Variable | Default | Purpose |
|
|
432
|
+
|---|---|---|
|
|
433
|
+
| `AIVISION_SERVER_URL` | unset | AI-Vision server for dots that name no `serverUrl` |
|
|
434
|
+
| `AIVISION_INTERNAL_TOKEN` | unset | Access token, sent as the `x-veesan-internal` header |
|
|
435
|
+
| `AIVISION_CHECKOUT` | unset | Absolute path of an AI-Vision checkout, for the MCP preset and Start |
|
|
436
|
+
| `AIVISION_PORT` | unset | Port to look for a running server on, and to start one on |
|
|
437
|
+
| `LOCALDOST_AIVISION_DETECT` | `1` | `0` turns off the search for AI-Vision on this machine |
|
|
438
|
+
|
|
439
|
+
## Test engines
|
|
440
|
+
|
|
441
|
+
Two engines can cover the **new scenarios** of a change: the What to test items of a PR review that no existing spec covers. The **Test engines** section of the Setup page lists both.
|
|
442
|
+
|
|
443
|
+
| Engine | What it runs | Ready when |
|
|
444
|
+
|---|---|---|
|
|
445
|
+
| **Playwright e2e** | Writes a real Playwright test for each new scenario from the running app, runs it, and keeps it when it passes or finds a bug | Chromium is installed on the server (`npx playwright install chromium`) |
|
|
446
|
+
| **AI-Vision** | Runs each new scenario as a plain-language browser case that a vision model drives and checks by sight | An AI-Vision server is connected, see [AI-Vision](#ai-vision) |
|
|
447
|
+
|
|
448
|
+
Each row shows **Ready** or **Not ready** with what is missing, one sentence about what the engine runs, and a **Default** switch. The switches set the **workspace default**, which every dot without an engine of its own uses:
|
|
449
|
+
|
|
450
|
+
| Switches on | Workspace default | Dot dialog and dot page |
|
|
451
|
+
|---|---|---|
|
|
452
|
+
| Both | `auto` | Playwright e2e, then AI-Vision |
|
|
453
|
+
| Playwright e2e | `playwright` | Playwright e2e only |
|
|
454
|
+
| AI-Vision | `aivision` | AI-Vision only |
|
|
455
|
+
|
|
456
|
+
At least one engine stays on. A dot can still choose **Both on every item** in its own settings.
|
|
457
|
+
|
|
458
|
+
**Existing specs.** The Playwright specs in your repo always run through the dot's test command. The engine choice never changes that. It covers new scenarios only.
|
|
459
|
+
|
|
460
|
+
**New installs.** The first time the server starts with nothing chosen, the default is `auto` when both engines are ready, otherwise the one that is ready (AI-Vision when neither is). While the default is chosen this way, it follows the engines: connect AI-Vision later and it becomes `auto`. Moving a switch saves your choice, and **Choose automatically** goes back. An install that already has runs starts on `aivision`, so its dots behave as before; change it on the Setup page. Demo mode (`LOCALDOST_DEMO=1`) stays on `aivision`.
|
|
461
|
+
|
|
462
|
+
**Dots.** In the dot dialog, under the Change reviewer stage, **Engine for new scenarios** reads **Uses the workspace default (<name>)** until you pick another choice: **Playwright e2e, then AI-Vision**, **Playwright e2e only**, **AI-Vision only**, or **Both on every item**. The header of the dot page shows the engines the dot uses, with a **Change** link. AI-Vision cases also need **AI-Vision tests** turned on for the dot.
|
|
463
|
+
|
|
464
|
+
| Variable | Default | Purpose |
|
|
465
|
+
|---|---|---|
|
|
466
|
+
| `LOCALDOST_TEST_ENGINE` | unset | `auto`, `playwright`, or `aivision`. Sets the workspace default. The environment wins: the Setup page shows the switches read-only |
|
|
467
|
+
|
|
468
|
+
The Setup page saves the default through `PUT /api/setup/engines` and reads it with `GET /api/setup/engines`, see [API-V2.md](API-V2.md#test-engines).
|
|
469
|
+
|
|
470
|
+
## True Platform
|
|
471
|
+
|
|
472
|
+
A dot with **Send to True Platform** on copies its work into one True Platform project: every test case it designs or edits, every automated run it makes, and every manual result a tester records (KAT-406). localdost stays the place where the work happens. True Platform becomes the record that test managers read. A slow or down True Platform never stops a localdost run.
|
|
473
|
+
|
|
474
|
+
The switch is off by default. It is a group on the dot settings (**True Platform**) with the switch, the address, the project ID, a connection check, and a Details fold. The switch, the address, and the project are the admin's, like the test command: an editor or a member sees them read-only with "Ask your localdost admin." Save them through `PATCH /api/dots/:id` with `trueSync: { enabled, tenantUrl, projectId }`, see [API-V2.md](API-V2.md#true-platform-sync).
|
|
475
|
+
|
|
476
|
+
| Variable | Default | Purpose |
|
|
477
|
+
|---|---|---|
|
|
478
|
+
| `LOCALDOST_KATALON_API_KEY` | unset | The True Platform API key. The environment of the server only: it is never stored in the database, never sent to the model, never given to a test process, and hidden in logs and errors |
|
|
479
|
+
| `LOCALDOST_KATALON_API_KEY_FILE` | unset | The path of a file that holds the key, used when `LOCALDOST_KATALON_API_KEY` is empty. The file is read when needed and kept for 5 minutes, and a change of its modified time or size is seen at once. Run `chmod 600` on it: a file that others can read still works, with a warning |
|
|
480
|
+
| `LOCALDOST_KATALON_HOSTS` | unset | A comma list of extra origins that may get the key, for example `http://127.0.0.1:4901`. The tests use it for a fake server. Leave it unset in real use |
|
|
481
|
+
| `LOCALDOST_KATALON_OPS_URL` | `https://testops.katalon.io` | The host of the upload service. Change it only for a test |
|
|
482
|
+
|
|
483
|
+
Neither key variable can be set from the app, and the Setup page does not read them. `GET /api/dots/:id` says `trueKey: { set, source }` and nothing else about the key.
|
|
484
|
+
|
|
485
|
+
**Where the key may go.** The key, its Basic form, and the bearer token that the login returns go only to `https://testops.katalon.io` and to `https://<name>.katalon.io` hosts: https, port 443, no user name in the address. The check runs on every request, so an address that was saved before this rule, or written into the database by hand, is still refused. A redirect from True Platform is not followed. The signed upload addresses that True Platform gives get no key. A saved address that is not https on a katalon.io host is refused with "Use your True Platform address, for example https://your-team.katalon.io."
|
|
486
|
+
|
|
487
|
+
**Check connection.** The button is an admin call (`POST /api/dots/:id/true/check`). It logs in, reads the key owner and the repository, and answers `Connected to "<project name>"`, or one sentence: `The key was rejected`, `Project 3517762 was not found for this key`, or `True Platform did not answer`. It checks the values on screen even before they are saved, and a good check of the saved project ends a hold.
|
|
488
|
+
|
|
489
|
+
**What goes where.** A folder `Test Cases/localdost/<dot name>` holds the dot. Its folders mirror the case tree of the dot, so the ticket folders of the test designer (`PROD-123 Short title` under `PROD`) stay as they are, and a case with no folder goes to `Unfiled`. The path is fixed at the first send, so renaming the dot does not move it. A case is named `<key> <title>`, with every character outside letters, digits, spaces, and `( ) . , _ - + & = '` turned into a space, and cut at 200 characters.
|
|
490
|
+
|
|
491
|
+
| localdost | True Platform |
|
|
492
|
+
|---|---|
|
|
493
|
+
| Case title, steps, expected results | Name, steps, expected results. Steps have no test data column in localdost, so it is sent empty |
|
|
494
|
+
| Priority must, should, could | HIGH, MEDIUM, LOW |
|
|
495
|
+
| Status draft, ready, retired | DRAFT, PUBLISHED, INACTIVE. A retired case is never deleted |
|
|
496
|
+
| Kind manual, playwright, aivision | MANUAL_ONLY, AUTOMATION_COMPLETE, AUTOMATION_COMPLETE, and the description says `Automated in localdost: Playwright, <spec path>` or `Automated in localdost: AI-Vision`. The case stays a manual case in True |
|
|
497
|
+
| Ticket refs | A Jira ref that True Platform has is linked as a requirement. True Platform has requirements only for Jira and Azure DevOps items its own integration syncs, so a Linear or GitHub ref is never looked up, and a Jira ref True does not have yet is looked for again every day for 30 days |
|
|
498
|
+
| (none) | The description ends with `localdost case TC-12 (id <case id>)`, and a link back to the case when the dot has `LOCALDOST_PUBLIC_URL` |
|
|
499
|
+
|
|
500
|
+
**Runs.** Every PR check, merge run, Tests repo run, rerun, and AI-Vision run that finishes with a report becomes one automated run in True Platform with a result for each test (passed, failed, skipped; flaky is passed with the line `Passed on retry <n>`, a time-out is failed with `Timed out`), its duration, and, when `LOCALDOST_PUBLIC_URL` is set, lines with the link to the run page, the video, and the trace. A test that a case owns is named `[TC-12] <test title>`. A run whose tests did not run sends nothing and says so. localdost replays its own report into the Katalon reporter (`@katalon/playwright-reporter`, pinned at 0.0.3) and uploads the files the way the reporter does: a login, signed URLs, a PUT per file, and an end report per suite. The key never enters the test process.
|
|
501
|
+
|
|
502
|
+
**Manual test runs.** A manual test run in localdost makes one suite and one run in True Platform (named `<name> (localdost <short id>)`), records each result with its step notes and a comment `Tested by <name> in localdost.`, and ends the run when the test run is marked done, with every case that has no result as SKIPPED. True Platform lets only the user who made the run record on it, and not on an ended run. A test run that is reopened after its True run ended starts a second round: a new run named `... round 2` in the same suite, with the latest result of every case. The page links to the newest round and lists the earlier ones under Details.
|
|
503
|
+
|
|
504
|
+
**Queue and retry.** Nothing is sent from the request that made the change. Each case, run, result, and end waits in a queue (the `true_outbox` table, kept apart from the job queue) with these rules:
|
|
505
|
+
|
|
506
|
+
- A network error, a time-out, a 5xx, or a 429 waits 1 minute, 5 minutes, 15 minutes, 1 hour, and then every hour (a `Retry-After` header wins when it is longer). An item gives up after 24 hours of failing, not counting the time the dot was held.
|
|
507
|
+
- Any other 4xx answer gives the item up at once with the server's words, cut to 300 characters and hidden of secrets. A 401 or 403 holds every item of the dot as "key rejected" and checks again after an hour, or at once when the key file or the variable changes. A missing key holds the dot until a key appears. A 404 on the project holds it as "project not found".
|
|
508
|
+
- When no call of a dot gets an answer for 5 minutes, the dot is held as "unreachable", its items stop aging, and one cheap call every 15 minutes finds out when True Platform is back. A day-long outage ends with the queue sending in order, and nothing gives up because of it.
|
|
509
|
+
- An item that depends on one that gave up (a result of a case that was not sent) gives up with "Not sent because TC-12 was not sent." **Send again** on an item, or **Send all again** in the Details of the setting, puts them back in the queue in order with a fresh 24 hours.
|
|
510
|
+
- At most 5 requests a second go out from the server.
|
|
511
|
+
- In [sandbox mode](#sandbox-mode) and in demo mode nothing leaves, and items are held with "Not sent: sandbox mode."
|
|
512
|
+
|
|
513
|
+
**The status line.** The run page, the manual test run page, and the case drawer each get one line under the title when the dot sends: `Sent to True Platform` with a link, `Sending to True Platform...`, `Not sent yet: True Platform did not answer. Trying again at 14:05.`, `Not sent: gave up after 24 hours.` with **Send again**, and the other states of the queue. Details lists each item that waits, with its last error in words and its number of tries.
|
|
514
|
+
|
|
515
|
+
**Not yet confirmed against the real service.** The shapes of the test case, folder, requirement, manual run, and result calls come from the Katalon MCP server source and earlier captures, and the tests use a fake server. The first live run needs the admin's go-ahead, in a scratch folder `Test Cases/localdost/_probe` of the chosen project. Open points: whether the tenant host takes the API key (`Authorization: Basic base64(":<key>")` with `x-project-id`) on the test case and manual run routes as it does on `/v2/schedules`, which field of the run list carries the execution uuid (until it is known the line links to the project's Executions page), and whether a second end report for the same suite adds results twice. `@katalon/playwright-reporter` has no licence field in its package: ask Katalon before a public release depends on it.
|
|
516
|
+
|
|
517
|
+
## Approvals and connections
|
|
518
|
+
|
|
519
|
+
| Variable | Default | Purpose |
|
|
520
|
+
|---|---|---|
|
|
521
|
+
| `LOCALDOST_APPROVAL_TTL_MS` | `86400000` (24 h), minimum 60000 | A pending approval expires after this long. Deciding it later returns 410 |
|
|
522
|
+
| `LOCALDOST_ALLOW_STDIO` | unset | `1` lets a non-loopback client add a stdio MCP connection while `LOCALDOST_TOKEN` is unset. See [CONNECTIONS.md](CONNECTIONS.md) |
|
|
523
|
+
|
|
524
|
+
## Compose-only variables
|
|
525
|
+
|
|
526
|
+
`compose.yml` reads two more variables from `.env`. `LOCALDOST_PORT` (default `4319`) is the host port. `CLOUDFLARE_TUNNEL_TOKEN` feeds the optional `tunnel` profile. The server itself does not read either.
|
|
527
|
+
|
|
528
|
+
## Dot fields
|
|
529
|
+
|
|
530
|
+
Dots live in the database, and you edit them in the app or through `POST /api/dots` and `PATCH /api/dots/:id`. The fields that connect a dot to the services above:
|
|
531
|
+
|
|
532
|
+
| Field | Example | Purpose |
|
|
533
|
+
|---|---|---|
|
|
534
|
+
| `repo` | `{ "owner": "acme", "name": "shop-web", "defaultBranch": "main", "pathFilters": ["web/**"] }` | GitHub repo the dot watches |
|
|
535
|
+
| `tracker` | `{ "kind": "jira", "baseUrl": "https://acme.atlassian.net", "projectKeys": ["SHOP"] }` or `{ "kind": "linear", "teamKeys": ["ENG"], "projectName": "Checkout" }` | Ticket source. `projectName` narrows Linear polling to one project, and is the project the dot files defects in |
|
|
536
|
+
| `baseUrl` | `https://staging.shop.acme.com` | App URL the tests run against, passed as `BASE_URL` |
|
|
537
|
+
| `runner` | `{ "mode": "local", "workdir": "/home/me/shop-web", "command": "npx playwright test", "passEnv": ["LOCALDOST_TEST_USER"] }` | See [SANDBOX.md](SANDBOX.md). `passEnv` names the host variables that carry the test login, see [Test login](SANDBOX.md#test-login) |
|
|
538
|
+
| `probes` | `[{ "url": "https://shop.acme.com/health" }]` | HTTP or browser probes. `expectStatus`, `expectText`, `timeoutMs`, `kind`, `selector`, and `steps` are optional |
|
|
539
|
+
| `probeIntervalSec` | `60` | Probe interval |
|
|
540
|
+
| `statusPages` | `["https://www.githubstatus.com"]` | Statuspage sites to poll |
|
|
541
|
+
| `rules` | `{ "ticket_comment": "act" }` | Rule level per action kind. Unset kinds use the defaults |
|
|
542
|
+
| `digestHour` | `18` | Local hour of the daily digest |
|
|
543
|
+
| `slackChannel` | `qa-alerts` | Channel for notices and for routing mentions |
|
|
544
|
+
| `connections` | `["<connection id>"]` | MCP connections attached to the dot |
|
|
545
|
+
| `review` | `{ "enabled": true, "blockOn": "high" }` | PR review options, see [PR-REVIEW.md](PR-REVIEW.md#options) |
|
|
546
|
+
| `stages` | `{ "designer": { "on": true, "rules": "Cover the empty cart." } }` | Switch and extra rules per stage: `analyst`, `designer`, `reviewer`, `regression`, `watcher`, `guard`. A save merges per stage. An empty `rules` removes them. See [STAGES.md](STAGES.md#5-rules-model-and-toggles-per-stage) |
|
|
547
|
+
| `stageModels` | `{ "reviewer": "codex-cli" }` | Model per stage: `claude-cli`, `codex-cli`, `claude-token`, or `anthropic`. `null` puts a stage back on the dot default |
|
|
548
|
+
| `stagesNotice` | `"dismissed"` | Closes the one-time offer of the new stages on a dot that predates them. The server sets the offer at boot, and a dot created later starts with it dismissed; a save can only dismiss it |
|
|
549
|
+
| `pollGithub`, `pollLinear` | `true` | Poll mode for this dot only. `POST /api/dots` sets `pollGithub` when the server has no webhook route. The routes check that `pollGithub` is true or false, and the setup flow always sends it |
|
|
550
|
+
| `shadowWeekDays` | `7` | Save-only. `POST` or `PATCH` holds the PR comment and the check run at Ask first for this many days (1 to 30), and stores the end date and the old levels in `shadowWeek`. `0` ends the week now. See [The shadow week](#the-shadow-week) |
|
|
551
|
+
| `testDocsOnlySkip` | `true` | A PR or push that changes only docs (`*.md`, `docs/**`), `CHANGELOG.md`, or the `version` field of `package.json` and `package-lock.json` still gets the review and report, but no test run. Default `true` |
|
|
552
|
+
| `testDebounceSec` | `120` | A PR check waits this many seconds of quiet before it starts. A newer head of the same PR drops the queued job and cancels a running one. `0` turns it off. Default `120`, maximum `3600` |
|
|
553
|
+
| `prTestTarget` | `"base"` | What a PR check tests against when no preview of the PR is known: `base` (default), `preview-only`, or `none`. See [PR tests without a preview](#pr-tests-without-a-preview) |
|
|
554
|
+
| `waitForPreviewSec` | `120` | A PR check holds up to this many seconds for a deployment of the PR head to be reported, then tests what it has: the preview if one came, else `baseUrl` as `prTestTarget` says. A whole number from 0 to 3600, default 0. See [PR tests without a preview](#pr-tests-without-a-preview) |
|
|
555
|
+
| `nightlyRun` | `{ "on": true, "time": "02:00", "days": [1, 2, 3, 4, 5] }` | The nightly test run of a dot with a Tests repo: off by default, `time` is `HH:MM` on the server's clock (default `02:00`), `days` are 0 (Sunday) to 6 (Saturday) (default Monday to Friday), `null` turns it off. A save that turns it on needs a Tests repo and a Base URL. See [Nightly test run](#nightly-test-run). `nightlyLastSlot` on the dot is the server's record of the last time it handled, and a save cannot write it |
|
|
556
|
+
| `testRepoOnPr`, `testRepoOnMerge` | `false` | Whether the Tests repo of the dot runs on app PRs and on merges. Both default to on, and `true`, `false` and `null` (back to the default) are the values the routes take. See [Tests repo](#tests-repo) |
|
|
557
|
+
| `waitForDeploySec` | `900` | A merge run waits up to this many seconds for a successful deploy of the merge commit to `deployEnvironment` before it tests. A whole number from 0 to 3600. `0`, `null`, and unset turn the wait off (the default). A wait needs `deployEnvironment`. See [Waiting for the deploy](#waiting-for-the-deploy) |
|
|
558
|
+
| `deployEnvironment` | `"qa"` | The GitHub deployment environment the merge deploys to, one line of up to 100 characters. The admin changes it and `waitForDeploySec` with the runner settings |
|
|
559
|
+
| `reuseMergeResults` | `true` | A merge whose commit has the same git tree as the PR head that last passed its tests reuses those results instead of running the suite again. Default `true` |
|
|
560
|
+
| `dailyModelCap` | `200` | Model calls the dot may make per day, counted from the local midnight of the server. At the cap new reviews of the dot wait for the reset and chat answers say so. A whole number from 1 to 100000; `0` and `null` turn the cap off (the default). See [Daily model cap](#daily-model-cap) |
|
|
561
|
+
| `trueSync` | `{ "enabled": true, "tenantUrl": "https://your-team.katalon.io", "projectId": 3517762 }` | Send the dot's test cases, runs, and results to a True Platform project. Off by default. The admin changes it. The server keeps its own state in the same object (`repositoryId`, `folderPath`, `ownerUuid`, `blocked`), and a request never sets those. See [True Platform](#true-platform) |
|
|
562
|
+
| `kind` | `"lead"` | `dot` (the default) or `lead`. Set on `POST /api/dots` only; a dot never changes kind. A lead names member dots and holds no repo, tracker, site check, runner, review options, or stages of its own. See [Lead QA dot](#lead-qa-dot) |
|
|
563
|
+
| `memberIds` | `["<dot id>", "<dot id>"]` | The dots a lead names: 1 to 8, in the order the team page lists them. A dot is in at most one lead. A save that leaves it out keeps the members |
|
|
564
|
+
|
|
565
|
+
### Lead QA dot
|
|
566
|
+
|
|
567
|
+
A Lead QA dot (KAT-405) gives a team of dots one page: the team health, a daily team digest, one place to ask about every member, shared test cases, one bug report per bug, and a release call. It holds no repos, tracker, site checks, or runner. Each member keeps its own repos, tracker, rules, and Inbox, and a lead changes none of them. Everything the lead shows is built from rows the members already store, and the lead posts nothing to GitHub or a tracker. The full spec is [LEAD-DOT.md](LEAD-DOT.md).
|
|
568
|
+
|
|
569
|
+
- **Make one.** On the Dots page choose **New Lead QA for a team** in the new dot dialog, name it, and add 1 to 8 dots in the order you want the team page to list them. Or send `POST /api/dots` with `{ "name": "Lead QA", "kind": "lead", "memberIds": ["<dot id>", "<dot id>"] }`. A dot can be in one lead only, a lead cannot be a member, and an archived dot cannot be added. Each refusal says which rule it broke.
|
|
570
|
+
- **The page.** The title row has three buttons: **Digest now**, **Ask** and **Settings**. **Pause** and **Remove dot** sit in a row at the foot of the page, and the goal (mission, tell me when, never) is edited in Settings. On a phone the page starts with the team word, then the three buttons, then one tap row per member, then the release call with its three first reasons and **See the reasons**.
|
|
571
|
+
- **Team health.** The lead page shows one status word for the team, the worst of its members (Failing, Needs you, Not watching, Healthy), and one row per member with its job and the one thing that needs a person. **Open** goes to the member's page, or to the fix the member's own health answer gives when it is a link (Setup, the bug already filed). A paused or archived member shows as such and does not count. The dot list shows the team word on the lead's row. The count at the top of the list leaves leads out, so one Failing member reads "1 needs you", not 2.
|
|
572
|
+
- **Daily team digest.** At the lead's **Digest time** the lead posts one message to its own chat and to its **Slack channel** when one is set. **Digest** says Every day or Every Monday, for the week. **Digest now** on the lead page makes the same message at once and shows it in your chat only. It is built by code, makes no AI call, and counts nothing against **Daily AI calls**, which cover the answers of the lead's chat only. When dots share a Slack channel, a message that names no dot goes to the lead, and a message that names a member goes to that member.
|
|
573
|
+
- **Ask the lead.** The lead chat answers about every member and routes an action to the member whose job it is: "Review PR 412 again" goes to the dot that reviews PRs, under its own rules and cap, and the answer says "Asked PR Reviewer to review PR 412." One turn sends at most 3 such requests.
|
|
574
|
+
- **Shared test cases.** A test case a member designed for a ticket is found by the other members: the PR check and the after-merge run use it, and a result lands on the case's own dot. A PR check of a dot with no tracker reads the tickets of its lead's trackers.
|
|
575
|
+
- **One bug, one draft.** When two members find the same bug, the Inbox holds one draft. It names both finders, and a rejection counts for both. Approving it files the bug the way a member's draft is filed. The lead has no Inbox of its own.
|
|
576
|
+
- **Release call.** The lead page and the chat answer "Can we release 2026.11?" with Ready, Ready with risk, or Not ready, and the reasons, from the members' requirements, bugs, results, and Inbox items. It never posts and says readiness only. With no milestone picked it answers for the nearest open milestone, or for the newest one when every requirement passes.
|
|
577
|
+
- **Removing members.** Archiving a member keeps it in the lead. Deleting a member takes it out of the lead and removes the test run items of other members that pointed at its test cases; the delete dialog lists them first. A lead keeps at least 1 member, so the last member cannot be deleted before the lead.
|
|
578
|
+
|
|
579
|
+
### Fewer test runs
|
|
580
|
+
|
|
581
|
+
Three settings keep a dot from running the suite when it learns nothing new (KAT-344). They are on by default, and `PATCH /api/dots/:id` turns each off: `{ "testDocsOnlySkip": false }`, `{ "testDebounceSec": 0 }`, `{ "reuseMergeResults": false }`. The route rejects a non-boolean switch and a `testDebounceSec` that is not a whole number from 0 to 3600. The run step "Tests skipped: only docs or the changelog changed" marks a skip, and the step "Same code as the tested PR head ..." marks a reuse and links the PR run. A merge finds its PR from the number in the commit title (`(#120)` or `Merge pull request #120`). The server variable `LOCALDOST_TEST_DEBOUNCE_DEFAULT_SEC` changes the default quiet period of every dot that sets none; the test scripts set it to 0.
|
|
582
|
+
|
|
583
|
+
### Run tests per repo
|
|
584
|
+
|
|
585
|
+
A repo entry can carry `runTests: false` (KAT-361). The dot then reviews the pull requests and merges of that repo and runs no tests for them, so a back end with no Playwright suite does not report "Tests did not run" on every PR. The run says "Tests skipped: Run tests is off for acme/api" and the verdict is a pass with no test counts. Only `false` is stored: a repo with no switch runs its tests. The switch applies to app repos; `POST /api/dots` and `PATCH /api/dots/:id` answer 400 for a test or docs repo, or for a value that is not a boolean. The dot dialog has it under Sources, next to the PR scope, and the Repos step of setup has it on each App card. More: [SANDBOX.md](SANDBOX.md#run-tests-per-repo).
|
|
586
|
+
|
|
587
|
+
### Tests repo
|
|
588
|
+
|
|
589
|
+
A team often keeps its end-to-end suite in a repo of its own, and its app repos have no Playwright suite. Give the dot that repo with the role **Tests** (Repos step of setup, or `repos` on `POST /api/dots` and `PATCH /api/dots/:id`), and it runs the suite after every PR check and every merge of an app repo, against the same app (KAT-86). The app repo's own tests, when it has any, run first. Turn Run tests off for an app repo with no suite: it is still reviewed, and the Tests repo is its test run.
|
|
590
|
+
|
|
591
|
+
```json
|
|
592
|
+
{
|
|
593
|
+
"repos": [
|
|
594
|
+
{ "repo": "acme/ai-platform", "role": "app", "runTests": false },
|
|
595
|
+
{ "repo": "acme/test-management", "role": "app", "runTests": false },
|
|
596
|
+
{ "repo": "acme/true-dogfood-tests", "role": "test", "test": { "framework": "playwright", "label": "True Platform", "passEnv": ["LOCALDOST_TEST_USER", "LOCALDOST_TEST_PASSWORD"] } }
|
|
597
|
+
],
|
|
598
|
+
"baseUrl": "https://qa.example.com",
|
|
599
|
+
"waitForDeploySec": 900,
|
|
600
|
+
"deployEnvironment": "qa"
|
|
601
|
+
}
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
What a person sees: the PR comment and the GitHub check each get one line, "Ran 24 True Platform tests from true-dogfood-tests: 23 passed, 1 failed.", and the failed test is a bug (or, on the shared QA build, a risk to test) that names the spec of the Tests repo. The run page lists each case as `owner/name:path`. The commit that ran, the ref, why that ref, the tests chosen and the URL are under Details for developers. All fields of `test` are in [SANDBOX.md](SANDBOX.md#tests-repo); the repos a Tests repo exercises are `targets` on its entry (default: every app repo of the dot).
|
|
605
|
+
|
|
606
|
+
**Which version of the tests.** The first rule that applies picks the branch or tag of the Tests repo for a PR check:
|
|
607
|
+
|
|
608
|
+
1. The PR description holds a line `localdost-tests-ref: <branch>` and the Tests repo has that branch. This covers a change that lands as two PRs, the app change and its tests. A branch the Tests repo lacks is said in a step and ignored.
|
|
609
|
+
2. The Tests repo's `ref` is `match_branch` and it has a branch with the name of the app PR's branch.
|
|
610
|
+
3. `ref` is `{ "fixed": "release-1" }`: that branch or tag. A fixed ref that does not exist stops the run with a sentence.
|
|
611
|
+
4. The default branch of the Tests repo, as GitHub reports it.
|
|
612
|
+
|
|
613
|
+
A merge uses rules 3 and 4 only: the branch of a merged PR is a branch of the past. A PR from a fork never uses rules 1 and 2, since its description and branch name are written by someone outside the team. GitHub gives the commit of the chosen ref, and the run tests that commit only.
|
|
614
|
+
|
|
615
|
+
**Where it runs.** A PR check runs it against the preview of the PR when one is known, else against the dot's `baseUrl` as `prTestTarget` says (see [PR tests without a preview](#pr-tests-without-a-preview): `none` and `preview-only` hold the Tests repo back like the app tests, and a failure on the shared QA build is no bug). A merge runs it against `baseUrl` after the deploy wait (see [Waiting for the deploy](#waiting-for-the-deploy)); an app repo with Run tests off waits for the deploy when its merge runs a Tests repo, since that suite tests the deployed build. Without a preview and without a `baseUrl` there is nothing to test against, so the run says so and skips the batch. A change of only docs skips it too.
|
|
616
|
+
|
|
617
|
+
**Which tests run.** The whole suite, unless `test.areas` maps the areas of the change to tests. `areas` maps an area name, as the PR review names it, to a Playwright `--grep` text: `{ "checkout": "checkout|payment" }`. When the review's regression scope is area or narrow and every changed area is mapped, only those tests and the quick tests (the `smokeGrep` text, `@smoke`) run. A wide scope, an area that is not mapped, a change with no review, and a selection that matches no test run the whole suite, since nobody can say what covers an unmapped area and a pass with no test is no pass.
|
|
618
|
+
|
|
619
|
+
**Switches.** The dot has two: **Run the Tests repo on app PRs** and **Run the Tests repo on merges** (`testRepoOnPr` and `testRepoOnMerge`), both on. They are in the Test runner section of the dot dialog, for a dot that has a Tests repo. A switch that is off says so in a step, and the other switch is not touched. `test.on` on the repo entry does the same per repo.
|
|
620
|
+
|
|
621
|
+
**Fork PRs and the login.** A fork PR does not run the Tests repo on this machine. In Docker mode it runs only when the Tests repo has no test login to hand over, because the preview of a fork PR is controlled by the fork's author; otherwise it is skipped with a sentence. A fork PR never picks the ref; see [Fork safety](SANDBOX.md#tests-repo). The suite gets the test login from `test.passEnv`, else from the dot's `runner.passEnv`; names must start with `LOCALDOST_TEST_`, see [Test login](SANDBOX.md#test-login). The command, the setup, the folder and the login of a Tests repo are the admin's, like the runner settings.
|
|
622
|
+
|
|
623
|
+
### Nightly test run
|
|
624
|
+
|
|
625
|
+
A dot with a Tests repo can run that suite on a timer (KAT-390), against its **Base URL**, with no PR or merge behind it. It is off by default. In the dot dialog the controls are under **Advanced**, in **Test settings** (Test runner section): the **Nightly test run** switch, the **Time** (server time) and the **Days** (Monday to Friday unless you pick others). The main section of the dialog has a **Nightly test run** line of its own, under the Tests repo switches. It says what the dot does and on which clock, such as "Runs the tests every weekday at 02:00, server time (Asia/Bangkok, UTC+7).", or "The tests do not run at night.", and its button opens Advanced on the switch. The hint under the **Time** field names the same clock (`timeZone` and `tzOffsetMin` of `GET /api/health`: the zone name of the server and the minutes its clock is behind UTC now). On the API it is `nightlyRun: { on, time?, days? }` (see the settings table above); a save that turns it on and has no Tests repo or no Base URL (a demo dot needs none) is a 400.
|
|
626
|
+
|
|
627
|
+
- **When.** The server checks every minute and at start. A time of day on an allowed day is one *slot*. A slot queues one `nightly_run` job, once: the job key holds the dot and the slot, and the dot records the last slot it handled (`nightlyLastSlot`), so a restart or a second check queues nothing twice. A slot that passed while the server was down runs at the first check after it starts when it is under 6 hours old, and the run says "Started late"; an older slot is lost. Turning the schedule on, or changing it, forgets the slots before the save: switching it on at 03:00 for 02:00 does not run at once. A save that sends the same schedule changes nothing, which the dot dialog does on every save.
|
|
628
|
+
- **What runs.** Every repo of role Tests in the dot, at the default branch of the Tests repo (or the fixed `ref` of its settings), with the same checkout, setup, command and test login as for a PR, and no selection: the whole suite. `test.on` and `targets` do not apply, since no app repo and no job named it. The report lists each case as `owner/name:path` and the run page shows the Tests repo line. The verdict is the one of a merge run: the model reads the report. A night with no result, because the Tests repo could not be checked out or its setup failed, says so as a verdict that says nothing about the app.
|
|
629
|
+
- **What it respects.** A paused or archived dot, a dot whose Regression runner stage is off, a Lead QA dot, and a server in sandbox mode (no timer at all) run nothing, and the slot is recorded as skipped, so resuming later does not bring it back. The job is queued like any other: it waits for a free place in the queue, stays queued while the disk is short, and waits for the reset when the dot has reached its daily AI limit (the run says "Waiting for the daily model cap"). Pause or archive after the slot was queued, and the job ends with no run. A dot that lost its Tests repo or its Base URL since the setting was saved gets a run that says "The nightly test run cannot start" and needs a person; a save that takes the Tests repo away turns the schedule off.
|
|
630
|
+
- **Where it shows.** Activity and the run list name the job beside the dot **Nightly check** (not "After merge": nothing was merged) and put the slot on the chip, "Fri 23 Oct, 02:00" (the run page and text updates say "Nightly test run, Fri 23 Oct, 02:00"). The model is told that nothing was merged and is asked for a headline with neither the word nightly nor the test counts, since the row shows both. It counts as a check in the morning digest (a pass is one more passed check; a failure is a bug found, as for a merge) and in the Room. A pass sends nothing. A failure, or a night that could not start, sends one alert (`push_notify`, under the dot's rule). The run can be retried with **Check again** like any run. It files no defect by itself.
|
|
631
|
+
|
|
632
|
+
**Not built yet.** Playwright is the only framework that runs. A PR or merge of the Tests repo itself starts no test run (it gets CI failure triage), a merged result is not linked to the Tests repo's own issues, and the review does not read the Tests repo's specs to pick tests. See [MULTI-REPO.md](MULTI-REPO.md#5-build-tasks).
|
|
633
|
+
|
|
634
|
+
### Daily model cap
|
|
635
|
+
|
|
636
|
+
A dot can have a daily cap on its model calls (KAT-362), so one busy dot cannot spend a shared model account. It is off by default. Set **Daily model calls** in the dot form (Model use), as one number in the Rules step of the setup flow, or send `{ "dailyModelCap": 200 }` to `PATCH /api/dots/:id` (`0` or `null` removes the cap).
|
|
637
|
+
|
|
638
|
+
- **What counts.** One answered call through the model guard is one call: a ticket analysis, a score, a safety review, a review or verifier pass, a generated spec, a chat answer. A call that fails writes nothing. A call the guard moves to another model counts once, for the model that answered. The mock model counts nothing. Each call is a row in `model_calls` with the dot, the run, the stage (or `chat`, or `other`), the backend, and the token counts when the backend reported them: Anthropic API and Claude CLI replies carry `usage`, and the Codex CLI reports it in its event stream. A backend that sends no count leaves the row with no tokens.
|
|
639
|
+
- **The day.** The count starts at the local midnight of the server (the time zone of the server process, the one `Daily digest at` uses) and starts over at the next one.
|
|
640
|
+
- **At the cap.** A review job of the dot (`pr_check`, `post_merge`, `ci_failure`, `ticket_review`, `test_design`, `rerun`) does not start. It goes back to the queue with its attempt, to start at the reset, and a run stays behind: an interrupted run with the step `Waiting for the daily model cap` and the line `Daily model cap reached, resumes at 00:00 UTC` (the time zone of the server, since the people reading it are often elsewhere). The run page says the review starts again at that time and offers no Retry, and the dot's health does not change. The prod watcher, the daily digest, and repo scans go on, since a cap must not silence an outage alarm; their calls still count. A run already going finishes, so a dot can end a day a few calls over its cap. One `Blocked: daily model cap reached` update goes out per dot per day.
|
|
641
|
+
- **Edits while it waits.** A ticket edited while its review waits for the cap keeps one review: the newer edit replaces the queued job of the same ticket for the same dot, keeps its wait for the reset, and the run that says `Waiting for the daily model cap` follows it. The reset then starts one review of the newest text and posts one draft, where it used to start one review per edit (KAT-408). A review that already runs is not stopped, and a review a person asked for stays asked, in the chat or Slack thread where it was asked too. An edit made before the queued one (a late or repeated delivery) is ignored. **Rerun** on a ticket review replaces the queued review of the same ticket the same way: the rerun reads the text of the queued review, which is the newest, and keeps its wait.
|
|
642
|
+
- **Chat.** A dot at its cap answers a question with the same line (server time zone included) and does not call the model. A question a stored view answers without the model still gets its answer.
|
|
643
|
+
- **Seeing it.** The dot page and the dot list show `Daily model cap reached, resumes at <time>` (the time in the viewer's clock), and the dot view carries `modelCap: { cap, used, reached, resetsAt }` while a cap is set. The Room page says `Waits for the daily AI limit to reset` on each held job, in the Next row of its dot card and on its Board card, and `waitsForLimit` in `GET /api/room` counts the jobs of a dot that wait for it.
|
|
644
|
+
- **Going on early.** Raising the cap above the calls made today, or turning it off, starts the waiting reviews at once.
|
|
645
|
+
|
|
646
|
+
The **Model use** card on the dot page shows calls and tokens today and over the last 7 local days (today included), per stage, with a totals row and, when a cap is set, how much of it is used. Tokens add input and output of the calls that reported them (cached input counts as input); the card says so when some calls reported none. `GET /api/dots/:id/model-usage` returns the same numbers: `{ cap, today, week, stages[], todayFrom, weekFrom }`. Rows older than 35 days are removed, and deleting a dot removes its rows. The card starts empty on a server upgraded from 0.9.0, since earlier calls were not recorded.
|
|
647
|
+
|
|
648
|
+
### PR tests without a preview
|
|
649
|
+
|
|
650
|
+
A PR check tests against a preview of the PR when one is known: the `deployment_status` webhook records the preview URL of a head, and a PR check uses it. A dot that polls GitHub never receives that webhook, so its PR has no preview, and the tests ran against the dot's `baseUrl`, a build that shared QA work deployed. A new spec of the PR then ran against an old build and failed, and the failure became a defect draft (KAT-363). `prTestTarget` says what a PR check does when no preview is known:
|
|
651
|
+
|
|
652
|
+
| Value | What a PR check does with no preview |
|
|
653
|
+
|---|---|
|
|
654
|
+
| `base` (default) | Tests run against `baseUrl`. The run says so in a "Tests run against the shared QA build" step, the report carries the sentence "Tests ran against the shared QA build, not a build of this PR." next to the counts (in the PR comment and the check), and with a failure it adds that the failure may be the PR's new or changed spec running against an older build. The failure is a risk to test, never a bug: the PR comment says "no bugs found, 1 risk to test" and lists it under Risks to test as "failed against the shared QA build", the check stays neutral, the verdict is needs attention with no suspected cause, and no defect and no high push follow. A risk the review found by reading the code, or one the PR's own CI reproduced, still counts. A merge never reuses the results |
|
|
655
|
+
| `preview-only` | Tests run only against a preview of the PR. A PR with no preview gets the review and no test run ("Tests skipped: this dot tests PRs only against a preview of the PR, and this PR has none") |
|
|
656
|
+
| `none` | A PR check runs no tests, even with a preview. The review still runs |
|
|
657
|
+
|
|
658
|
+
A PR with a preview is tested on it under every value, as before. A dot can hold the check for the preview: with `waitForPreviewSec` above 0, a PR check whose tests will run waits up to that many seconds for the deployment of its head to be reported (a `deployment_status` delivery, which a dot on webhooks gets), then tests the preview if it came, else the base URL as the value above says. The run shows the steps "Waiting for the PR preview" and then "PR preview found" or "No PR preview appeared". The check keeps its place in the queue while it waits, and a cancel stops the wait. A PR whose tests are skipped (`none`, docs only, Run tests off with no Tests repo) does not wait. The Tests repo of the dot is tested against the same target. A `baseUrl` on this machine (`localhost`, `127.0.0.1`, `[::1]`) and a dot with no `baseUrl` are the PR's own build, since the test project starts the app from the checkout of the PR, so they carry no label and keep drafting defects; the same holds for the demo shop. Merges and pushes are not affected: `post_merge` runs its tests under every value. A review a person asks for follows the same value. In the app the setting is Tests on PRs, in the Test runner section of the dot settings. The pilot default is `base` with a QA base URL; choose `preview-only` when the repos deploy a preview per PR, and `none` for a repo that has no QA build to test against yet.
|
|
659
|
+
|
|
660
|
+
### Waiting for the deploy
|
|
661
|
+
|
|
662
|
+
A merge reaches the QA environment a few minutes after it lands. A merge run that starts at once tests the build from before the merge (KAT-368). With `waitForDeploySec` above 0 and a `deployEnvironment`, the post-merge job of a merge into the default branch waits for a successful deployment of the merge commit, or of a later commit that contains it, to that environment. The tests start after that. The dot form has Deploy environment and Wait for deploy in the Test runner section, after Test branches.
|
|
663
|
+
|
|
664
|
+
- **What counts as deployed.** All the deployments of one commit in the environment are one set, since one push may deploy several services, each as its own deployment. The merge is deployed when every deployment of the set has a `success` status. The set is the one for the merge commit, or the one for a later commit that GitHub says contains the merge commit (the compare of the merge commit against it says behind or identical) and that is on the default branch (the compare of the default branch against it says behind or identical). A feature branch built on the merge holds the merge commit too, with code that is not merged, so a deploy of it does not count until that commit is on the default branch. A deployment that GitHub marked `inactive` because a newer one replaced it counts, since it was deployed. A failed deployment that was retried is not a failed service: only the newest attempt of each service counts, so a failure followed by a retry that succeeded is deployed. Attempts of one service are told apart by the service the deployment names in a field made for it (`payload.service`, `name`, `app`, `component` or `project`, else a `task` other than `deploy`) and, when it names none or only the description does, by the time: a deployment of the same commit and environment that GitHub made after the failure was written replaces it, and one made before it (another service deployed in parallel) does not. For a service named in `payload` or `task` the newer deployment (the higher id) replaces the failure of the older one even when it was made before that failure was written, since a retry is often made while the first attempt still runs. A name read from the description is a guess, since parallel jobs of one pipeline share it (`Deploy (job 1)` and `Deploy (job 2)` are both `deploy`), so there the time decides: a job made before the failure was written is another job, and its success does not hide the failure. A description that carries a run, attempt, build or job number, a `#number`, a long number, a commit, or a time names the service without them (`Deploy web (run 8841)` and `Deploy web (run 8842)` are the service `deploy web`), so a retry made after the failure replaces the failed attempt. When the deployments of the merge commit itself cannot be read on GitHub, the look goes on with the later commits of the environment: one that holds the merge and deployed ends the wait as deployed, a failure that a delivery reported ends it as failed only after the later commits were read, and a later commit that cannot be read keeps the wait going. A set with a deployment that still runs is not deployed yet, and a `deployment_status` that says one deployment of the set succeeded does not end the wait by itself. Each look reads the statuses of every deployment it needs, one GitHub call each, so a deployment that has succeeded is read once and kept in memory: a later look reads only the deployments that have not succeeded yet.
|
|
665
|
+
- **A failed deploy.** When the newest attempt of one deployment of the set ends in `failure` or `error`, the wait stops and nothing tests, but only after the later commits that contain the merge were looked at: a later commit on the default branch that deployed successfully is the deploy of the merge, and the tests run. When GitHub cannot list the later commits, the wait goes on and the next look decides. The run shows the step `The QA deploy of this merge failed, so the tests did not run.` (the environment `qa` reads as QA, any other name as typed), with the commit and the deployment id under Details. The run ends as cancelled by the failed deploy, the GitHub check of the commit closes as neutral with the title `The QA deploy failed`, and the job is done. No test ran, so there is no verdict about the product and no defect is drafted. A deployment of a later commit that holds the merge commit and fails stops the wait the same way, unless a deployment of the merge commit itself is still running: that one may still succeed, so the wait goes on, and a later commit that deployed still stands in for it. A failed deployment of a commit that does not hold the merge says nothing about it. A check a person asks for does not wait for a deploy.
|
|
666
|
+
- **How the dot finds out.** With webhooks, a `deployment_status` success for the repository and the environment wakes the job at once. The job also looks at GitHub every 5 min, so a delivery the server missed is found. With polling there is no such delivery, so the job looks every 30 s. A look reads `GET /repos/{owner}/{repo}/deployments?sha=<merge commit>&environment=<environment>` and the statuses of each deployment, then the newest 5 deployments of the environment for a later commit. A `deployment_status` of `failure` or `error` wakes the job too, so a failed deploy is found at once and not at the next look. A delivery that says a deployment succeeded counts for itself only when GitHub lists no deployment of that commit, or cannot be read. A delivery that says one failed or errored stands the same way: when GitHub cannot be read or lists nothing of the commit, the wait stops on it, and one failed delivery of a commit outweighs a success of another service of that commit, whichever came last. When GitHub lists the commit, its list decides. The GitHub connection needs the Deployments: read permission, see [GITHUB.md](GITHUB.md#permissions). A look that fails is not an answer: the job waits on, and the step at the end names the error.
|
|
667
|
+
- **No queue slot.** A look is a short visit to the queue. When the merge is not deployed yet, the job goes back to the queue with `runAfter` set to the next look, the way a review waits for the daily model cap, and the slot is free for other jobs. A waiting job survives a restart, and its time counts from the first look.
|
|
668
|
+
- **On the run page.** The run of the merge starts with the wait. The Feed and the run page show Waiting for deploy, and the run has the step `Waiting for the qa deploy` with the merge commit and the time: "Merge commit abcdef0 has no successful deploy to qa yet. Waited 4 min 10 s of 15 min so far." Each look rewrites that step. The run has no verdict and no Retry, and the dot's health does not change. The GitHub check of the commit says "Waiting for the qa deploy" in place of "waiting for a free runner". When the deploy is found, the step `Deployed to qa` follows, and the same run goes on and tests. When a deployment of the set gives an `environment_url` (an `https` address, or `http` on this machine) that is the root of a site (never a path such as `/health`) on the origin of the dot's `baseUrl`, or on the host or origin the repo names in `deployUrlMatch`, the tests of the merge run against that address and the step says so ("The tests start now, against https://platform.qa.example.com."). The address is kept without its query string, fragment and sign-in, and one over 300 characters is not used. GitHub gives this address, and anyone who can write a deployment status in the environment chooses it, so a dot that has a test login (`runner.passEnv`, a file the dot copies in with `runner.copyFiles`, a Tests repo's `passEnv`, or an AI-Vision credential variable) uses it only when it is on the site of the dot's `baseUrl`: the same host or a subdomain of it. For any other address the step says so as a warning and the tests run against the `baseUrl`, so the login never goes there. To let a deploy address of another host take the login, set the `baseUrl` to a parent site such as `https://example.com`. Without one they run against the dot's base URL as before. A base URL with a path (`https://web.qa.example.com/app`) keeps its path: a deployment that gives the bare address of the site (`https://web.qa.example.com`) makes the target `https://web.qa.example.com/app`, and an address of the same site with another path is not used. Anyone who can write a deployment status to the repo can set that address. A dot with no test login runs its tests against whatever address the status gives, so set `environment_url` in your deploy workflow only to an address you trust with your tests. On a phone the Feed row says Waiting for deploy in words too. The run began when the wait began, so Started shows the merge, and Took leaves the wait out: it counts the tests, not the minutes before them.
|
|
669
|
+
- **When the time is up.** The run tests anyway, after the step `Tested before the QA deploy finished; the build may be older than this merge`. Its detail says how long it waited and for which commit.
|
|
670
|
+
- **A newer merge.** A newer merge into the same branch of the same repo ends the wait of the older one, when the newer merge waits for its own deploy. Its job ends, and its run is cancelled with the step `Superseded by a newer merge`, which names the merge that tests instead. A merge that does not wait, such as a docs-only merge the dot skips, tests nothing, so it ends no wait. Newer means later in the branch: GitHub's compare of the two merge commits says which one holds the other. The order of the events says nothing, since each merge sends a pull request event and a push event and a delivery can be late. When a late event of an older merge arrives, the older merge ends and the newer one goes on. When GitHub cannot say which is later, both keep waiting and each tests its own deploy. One merge that arrives twice is not a newer merge.
|
|
671
|
+
- **No wait.** A merge run starts at once when a person asked for it (a review on demand, a rerun), when the merge is into a branch that is not the default branch (a release branch listed in `testBranches`), when the repo has Run tests off and its merge runs no Tests repo (an app repo with Run tests off still waits when its merge runs a Tests repo, since that suite tests the deployed build), when the merge changes only docs and the dot skips those (`testDocsOnlySkip`), and on a server with no GitHub connection. A job that waited does not wait a second time when the queue runs it again.
|
|
672
|
+
- **While a job waits.** Pausing the dot stops the wait at once, on webhooks and on polling: the run is cancelled with the step `Stopped waiting for the deploy`. Archiving the dot does the same. Saving the wait (off, or another environment) makes the waiting jobs look again now, and a wait that was turned off lets their runs test at once.
|
|
673
|
+
|
|
674
|
+
#### Deploy wait of one repo
|
|
675
|
+
|
|
676
|
+
A dot with several app repos often has repos that deploy to different environments, or to none that GitHub reports. Each app repo may set its own `deployEnvironment` and `waitForDeploySec` in `repos`. The dot's two fields are the default for a repo that sets none, and a field left out falls back on its own: a repo with only `deployEnvironment` waits the dot's seconds on its own environment. A repo with `waitForDeploySec: 0` does not wait while the dot waits for the others, and a repo can wait when the dot does not (it needs an environment from itself or from the dot). A merge waits on the settings of its own repo, and its look reads that repo's deployments of that environment. A push that deploys several services to one environment gives several addresses, and an API host such as `https://api.qa.example.com/health` is not the page the tests open: `deployUrlMatch` names the host (`web.qa.example.com`) or the origin (`https://web.qa.example.com`) of the site, an admin setting like the other deploy fields. With no `baseUrl` and no `deployUrlMatch`, an address is used only when every deployment that gives one gives the same origin.
|
|
677
|
+
|
|
678
|
+
```json
|
|
679
|
+
{
|
|
680
|
+
"waitForDeploySec": 900,
|
|
681
|
+
"deployEnvironment": "qa",
|
|
682
|
+
"repos": [
|
|
683
|
+
{ "repo": "acme/shop", "role": "app", "deployEnvironment": "staging", "waitForDeploySec": 600 },
|
|
684
|
+
{ "repo": "acme/api", "role": "app", "waitForDeploySec": 0 },
|
|
685
|
+
{ "repo": "acme/payments", "role": "app" }
|
|
686
|
+
]
|
|
687
|
+
}
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
In the dot form, a dot with two or more app repos has a row for each repo under Advanced, in the Test runner section after Wait for deploy (the fold is closed unless a repo already has its own wait, and a save that fails on one of its fields opens it): its Environment and its Wait (seconds). An empty field shows the dot's value as its placeholder and uses it. A dot with one app repo has no rows, since the dot's two fields are that repo's. The routes and the form refuse seconds outside 0 to 3600, an environment over 100 characters or on several lines, deploy fields on a test or docs repo, and a repo that waits with no environment of its own or the dot's. The form and the route `repo` of the first app repo carry its two fields too, and a body with only `repo` that leaves them out keeps the ones the repo has.
|
|
691
|
+
|
|
692
|
+
The wait is the admin's with the runner settings, since it decides when a run starts on the server. This holds for the fields of each repo too. An editor sees all of them read-only in the dot form, and `PATCH /api/dots/:id` answers an editor who changes either with a 403, the code `runner_admin_only`, and "Ask your localdost admin to change the deploy wait." A save that sends the same values back is a save. A pilot setup: `deployEnvironment` the name your deploy workflow gives the QA environment (`qa`), and `waitForDeploySec` a little above the longest deploy you have seen.
|
|
693
|
+
|
|
694
|
+
### Path filters
|
|
695
|
+
|
|
696
|
+
A repo entry can carry `pathFilters`, a list of globs such as `["web/**"]` (a plain folder like `web` covers everything inside it). With filters set, a PR or push reaches the dot only when at least one changed file matches a filter, so two dots can split one repo by area. Push payloads list their files. A PR's file list is read from GitHub once per head and shared by every dot on the repo. A repo entry without `pathFilters` takes every event. If the file list cannot be read (network, auth, or a push payload past GitHub's 20-commit cap), the event still matches and the run shows a "Path filters not applied" step. Polled PRs carry no file list, so they match every dot until the webhook path is used. The dot settings show the filters in the repo form.
|
|
697
|
+
|
|
698
|
+
### PR scope
|
|
699
|
+
|
|
700
|
+
Several dots, or several teams, can share one repo that most of the PRs are not theirs. A repo entry can carry `prScope` to say which PRs the dot takes (KAT-354):
|
|
701
|
+
|
|
702
|
+
| Field | Example | What it does |
|
|
703
|
+
|---|---|---|
|
|
704
|
+
| `authors` | `["alice", "bob"]` | GitHub logins, case-insensitive. A PR by one of them is in scope |
|
|
705
|
+
| `team` | `"acme/web-team"` | A GitHub team slug. A PR by a member is in scope. The members are read from GitHub through the connected token or App and kept for 10 minutes. That needs the organization permission Members: read, see [GITHUB.md](GITHUB.md#permissions) |
|
|
706
|
+
| `linkedIssueLabels` | `["pod:agentic"]` | Labels, case-insensitive. A PR is in scope when an issue or ticket it links carries one of them. The links are the ones the PR check records: a closing keyword in the title or body (`Closes #12`, `Fixes acme/product#7`), an issue GitHub lists as closed by the PR (the Development sidebar), a link an issue timeline recorded, an issue the title or body mentions with no keyword (`Part of acme/product#412` or an issue URL; a bare `#412` is no mention, since it also matches a color or a step, and counts only after a closing keyword; for the scope only, since the PR check records no link and closes nothing for it; a mention that names no issue the token can see, such as a private repo, a deleted issue or a pull request, is ignored and never makes the rule unread; at most 10 issues are read per PR, closing links first), and, on a dot with a Jira or Linear tracker, a ticket key of that tracker in the title, branch or body (`TO-1234`). GitHub issue labels are read through the issues API, ticket labels through the dot's tracker connection |
|
|
707
|
+
| `skipDrafts` | `true` | A draft PR is not reviewed. When it leaves draft state it is, because `ready_for_review` queues a PR check |
|
|
708
|
+
| `skipBots` | `true` | A PR whose author is a Bot account or whose login ends in `[bot]` is not reviewed |
|
|
709
|
+
|
|
710
|
+
`authors`, `team`, and `linkedIssueLabels` are alternatives: with any of them set, a PR needs to match one. `skipDrafts` and `skipBots` keep PRs out whatever else matches. A repo with no `prScope` takes every PR. The scope applies to the PRs of app repos: a PR to review, a merged PR, and the push that brings a merge into the default branch. A push has no author, so the dot reads the merged PR of the head commit from GitHub (one call per push, only for a repo with a scope) and applies the same rules to it. That keeps the post-merge run of a PR the scope kept out from starting, with webhooks and with polling alike. A direct push has no PR and runs as before. A push that carries several merges follows the newest one. A listed test branch is not scoped.
|
|
711
|
+
|
|
712
|
+
A PR the scope keeps out is recorded, never dropped without a trace: the event row and the log say `out of scope: <rule>` (`draft`, `bot`, or the inclusion rules that were set, for example `authors, team`), and the webhook answer carries the reason. When GitHub or the tracker cannot say (a token with no access to the org's teams, a linked issue or ticket it cannot read), the PR still runs, but the dot does not trust it (KAT-363): the log says so once, the run shows a "PR scope not applied" step with what could not be read, its PR comment and check wait in the Inbox with the same reason (under any rule that would post on its own), and the dot list and the dot page show "PR scope not applied: could not read the members of team acme/web" until a later PR reads the rule. The dot view carries it as `scopeNotApplied` (`reason`, `hint`, `at`); the server keeps it in memory, so a restart clears the line and the next PR that cannot be read brings it back. A review you ask for (the dot page or chat) always runs, and so does a simulated event. The dot settings show the scope in the repo form under Path filters. The new dot dialog (a PR scope row) asks for it too, and the Repos step of setup asks for authors, skip drafts, and skip bots per app repo, so a dot's scope is set before its first poll runs. `POST /api/dots` and `PATCH /api/dots/:id` check it and answer 400 for a login or team slug that is not valid, an unknown rule, or a scope on a test or docs repo.
|
|
713
|
+
|
|
714
|
+
`npm run seed -- --help` lists flags that create a dot from the shell.
|
|
715
|
+
|
|
716
|
+
## Release notes and evidence packs
|
|
717
|
+
|
|
718
|
+
Both read what the dots already stored. They have no setting, no variable and no cost: neither makes a model call or asks a tracker.
|
|
719
|
+
|
|
720
|
+
- **Release notes** is a page in Reports (**Release notes**, `#/reports/release-notes`). It lists the note of every merged PR a dot reviewed, grouped by milestone and then by the issue the PR names, with a Breaking flag, who the note is for (Users, Admins or Developers) and what the tests said (Passed, Bug found, Partly tested or Not tested). Pick a dot or a milestone at the top. It is a table on a computer and a list on a phone. A note exists only when an after-merge check reviewed the merged PR and found the change user-visible. An issue has a milestone only when a ticket review or the GitHub poller read it.
|
|
721
|
+
- **Evidence pack** is a page for a check that names an issue (**Steps, screenshots and video** on the run page, `#/runs/<id>/evidence`): the issue, each test with its video and its steps with one screenshot each, the verdict and links. **Copy JSON** copies the same data as the API. The steps are in the order they ran, the step a failed test stopped at says "Failed here", and each screenshot has a Full size link. It shows steps and screenshots when the dot keeps them: a Playwright run saves screenshots after each step and records video for tests that fail, and set **Keep video of** to Every test in the dot's Test runner settings to keep the video of passing tests too (see [Sandbox](SANDBOX.md)).
|
|
722
|
+
|
|
723
|
+
A skill reads the same data with the server token (`LOCALDOST_TOKEN`), as the rest of the API:
|
|
724
|
+
|
|
725
|
+
```bash
|
|
726
|
+
curl -s -H "Authorization: Bearer $LOCALDOST_TOKEN" "$LOCALDOST_URL/api/v2/release-notes?dot=PR%20Reviewer&milestone=2026.11&format=md"
|
|
727
|
+
curl -s -H "Authorization: Bearer $LOCALDOST_TOKEN" "$LOCALDOST_URL/api/v2/runs/$RUN_ID/evidence-pack"
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
The screenshot and video paths in a pack (`/artifacts/<runId>/<file>`) need the same header. The pack hides tokens, cookie values and passwords in every text (a value a test typed too), and leaves out the trace, the test command and the test output. It cannot read pixels, so check a screenshot of a page that shows a secret before you share it. The fields and rules are in [API-V2.md](API-V2.md#release-notes-kat-391) and [API-V2.md](API-V2.md#evidence-pack-kat-392).
|
|
731
|
+
|
|
732
|
+
## Troubleshooting
|
|
733
|
+
|
|
734
|
+
**`localdost doctor` reports FAIL.** Each FAIL line carries a `fix:` command. Run it, or ask the person who owns the login to run it, then run `localdost doctor` again until it exits 0. `warn` lines are optional services.
|
|
735
|
+
|
|
736
|
+
**The server will not start: "Refusing to listen on HOST=... without LOCALDOST_TOKEN".** Without `LOCALDOST_TOKEN` the API has no auth, so the server listens on 127.0.0.1 only. Set `LOCALDOST_TOKEN` (`openssl rand -hex 24`) before you set `HOST` to a LAN address or run the container image, which sets `HOST=0.0.0.0`.
|
|
737
|
+
|
|
738
|
+
**The app gets 403 "LOCALDOST_TOKEN is unset, so the API answers on localhost only".** You opened the app through a tunnel, a LAN address, or a proxy while the server has no token. Set `LOCALDOST_TOKEN`, restart, and enter the token in the app's Settings. The Setup page needs the same access.
|
|
739
|
+
|
|
740
|
+
**`vite build` fails with "Tsconfig not found @ljharb/tsconfig".** The Rolldown transform looks for a tsconfig that includes each client file. The root `tsconfig.json` covers only `src/**/*.ts`, so without an explicit tsconfig the search walks past the project into parent folders and picks up any stray `tsconfig.json` there. One such file sits in `~/Library/Mobile Documents/iCloud~md~obsidian/`, above an iCloud vault, and extends a package that is not installed. `vite.config.ts` sets `tsconfig: tsconfig.client.json`, which stops the search. If a build still fails this way, look for a `tsconfig.json` in a parent folder of the checkout, or build from a clone outside iCloud, for example `~/code/localdost`.
|
|
741
|
+
|
|
742
|
+
**The model check fails with `LOCALDOST_LLM=claude-cli`.** Run `claude auth login` in your own terminal, or set `LOCALDOST_CLAUDE_BIN` to the full path of `claude`. On a server where nobody can sign in, connect the Model row with a subscription token from `claude setup-token` instead. `localdost init --skip-model-check` skips the round trip.
|
|
743
|
+
|
|
744
|
+
**A row shows Set by environment and has no Edit or Disconnect.** An environment variable sets that service, and the environment wins over values saved in the app. Remove the variable from the shell, `.env.localdost`, or `.env`, restart the server, and connect the service from the Setup page.
|
|
745
|
+
|
|
746
|
+
**Connect in the app returns "This server does not support connecting from the app yet".** The server is older than the app. Update localdost, or set the variables listed in the service's section above.
|
|
747
|
+
|
|
748
|
+
**Saved connections are gone after a move to a new host.** Saved values live in the data folder, encrypted with a key in the same folder. Copy the whole `LOCALDOST_DATA_DIR` to the new host, or connect the services again.
|
|
749
|
+
|
|
750
|
+
**The model check fails with `LOCALDOST_LLM=codex-cli`.** Run `codex login` in your own terminal, or set `LOCALDOST_CODEX_BIN` to the full path of `codex`. `localdost doctor` runs `codex login status`, which spends no usage. A "Model fallback" step on a run names the Codex error, for example a model your account cannot use or a usage limit.
|
|
751
|
+
|
|
752
|
+
**Setup says "Model unavailable: spend cap reached".** The provider refused the call, and localdost is waiting before it tries that model again. Ask the owner of the workspace to raise the cap or wait for it to reset, or switch models in the Model sheet. Runs that stopped have a Retry button.
|
|
753
|
+
|
|
754
|
+
**Webhooks return 503.** The secret for that source is unset. The answer names the setting (`GITHUB_WEBHOOK_SECRET`, `JIRA_WEBHOOK_SECRET`, or `LINEAR_WEBHOOK_SECRET`). Set it to the secret on the webhook, or use polling. For a local demo only, `LOCALDOST_ALLOW_UNSIGNED_WEBHOOKS=1` accepts unsigned events and logs a warning at start.
|
|
755
|
+
|
|
756
|
+
**Webhooks return 401.** The secret in localdost and in the webhook settings differ. Linear deliveries more than 60 seconds off the server clock are also rejected, so check the server clock.
|
|
757
|
+
|
|
758
|
+
**A run says "Tests did not run: no test report came back".** The test command must honour `--reporter=json`. localdost appends `--reporter=json` and reads the report from `PLAYWRIGHT_JSON_OUTPUT_FILE`. A command that wraps Playwright in a script must pass the arguments through. The command runs without a shell, so install and build steps go in Setup steps (`runner.setup`); see [Setup steps](SANDBOX.md#setup-steps). **Try it** in the dot dialog runs both once and shows the output.
|
|
759
|
+
|
|
760
|
+
**A Docker run fails with "Executable doesn't exist".** The image tag and the checkout's `@playwright/test` version differ. localdost pins the image to the version in the checkout's `package.json`. Set `runner.image` to a matching `mcr.microsoft.com/playwright:v<version>-noble` image.
|
|
761
|
+
|
|
762
|
+
**The app shows "Disk almost full" or "Disk full".** New test runs wait until there is room. See [Disk space](#disk-space) for what localdost does and how to free space.
|
|
763
|
+
|
|
764
|
+
**A screen says "localdost is slow to answer."** The files of that screen did not arrive in time, usually because the server host is short of memory or swapping. The app asks for a file again after 1 second and after 3 seconds, each time on a new `?retry=` address, because the browser keeps the failure of an address it already asked for. The message shows at most 45 seconds after you opened the screen, and a screen that arrives later replaces it.
|
|
765
|
+
|
|
766
|
+
A screen can also fail because of a file that other screens share. The browser names only the chunk that was asked for, and it keeps the failure of the shared file until the page closes. So the app finds the files of the chunk that do not load, asks for those on a new address, and runs the chunk from a copy that points at them. A `Content-Security-Policy` in front of localdost that leaves `blob:` out of `script-src` stops that copy from running, and **Reload page** is the way out.
|
|
767
|
+
|
|
768
|
+
The **Try again** button starts over only the screen on display, and it becomes **Reload page** when it fails a second time. It loads or waits for the styles of that screen first, so the screen never opens without them. When you leave a screen that failed and come back, the app asks the server again with no button to press. Check the load of the host (`uptime`) before you look at localdost.
|
|
769
|
+
|
|
770
|
+
**A list says "Still loading. localdost is slow to answer right now." under its loading rows.** The list request has waited 10 seconds. Activity, Inbox, Reports, Dots, Room, Test cases, Test runs, and Traceability do this, and the Inbox says it under both of its lists, To review and Decided. A list that was already waiting when you opened its screen shows its loading rows at once, together with the line, and so does the Decided list of the Inbox when To review has waited. The line goes away when the rows arrive, and nothing is retried: the request is still running.
|
|
771
|
+
|
|
772
|
+
**A screen says "localdost was updated. Reload to get the new version."** The page in your browser was built by an older version, and the server no longer has the files it asks for, because every build renames them. Press **Reload**. The server answers its app page for a path it does not know, so a file that is gone can come back as that page instead of a 404, and the app treats both the same. Safari does not say which file failed, so there the app asks the server for its app page and compares the script it starts with the one your page started with: when they differ, the page is from an older build. A screen that fails while it draws is a different case and still says "This screen crashed".
|
|
773
|
+
|
|
774
|
+
**Port 4319 is in use.** `localdost init` picks the next free port when 4319 is taken. Set `PORT` in `.env.localdost` to choose one.
|
|
775
|
+
|
|
776
|
+
**Deciding an approval returns 410.** It expired. Rerun the run to get a new draft, or raise `LOCALDOST_APPROVAL_TTL_MS`.
|