@atolis-hq/wake 0.2.97 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/activities/agent/agent-activity-definition.js +26 -0
- package/dist/src/activities/agent/agent-activity.js +171 -0
- package/dist/src/activities/agent/agent-result.js +149 -0
- package/dist/src/activities/contracts/activity.js +10 -0
- package/dist/src/activities/contracts/config.js +2 -0
- package/dist/src/activities/contracts/event-schema.js +302 -0
- package/dist/src/activities/contracts/events.js +42 -0
- package/dist/src/activities/contracts/identifiers.js +29 -0
- package/dist/src/activities/contracts/registry.js +71 -0
- package/dist/src/activities/contracts/streams.js +17 -0
- package/dist/src/activities/contracts/vocabulary.js +58 -0
- package/dist/src/activities/index.js +21 -0
- package/dist/src/activities/pr/activity-support.js +111 -0
- package/dist/src/activities/pr/application.js +158 -0
- package/dist/src/activities/pr/approve.js +85 -0
- package/dist/src/activities/pr/capability.js +11 -0
- package/dist/src/activities/pr/decision-claim.js +76 -0
- package/dist/src/activities/pr/event-drafts.js +66 -0
- package/dist/src/activities/pr/intent.js +37 -0
- package/dist/src/activities/pr/merge-authority-gate.js +14 -0
- package/dist/src/activities/pr/merge.js +110 -0
- package/dist/src/activities/pr/policy.js +135 -0
- package/dist/src/activities/pr/projection.js +130 -0
- package/dist/src/activities/pr/vocabulary.js +30 -0
- package/dist/src/activities/review/authorization.js +22 -0
- package/dist/src/activities/review/contracts.js +22 -0
- package/dist/src/activities/review/signals.js +3 -0
- package/dist/src/bootstrap/analytics-projection.js +26 -0
- package/dist/src/bootstrap/board-projection.js +337 -0
- package/dist/src/bootstrap/composition-root.js +382 -0
- package/dist/src/bootstrap/config/load-config.js +51 -0
- package/dist/src/bootstrap/config/root-schema.js +134 -0
- package/dist/src/bootstrap/external-ref.js +12 -0
- package/dist/src/bootstrap/fake-provider-files.js +44 -0
- package/dist/src/bootstrap/fake-scenarios.js +22 -0
- package/dist/src/bootstrap/index.js +48 -0
- package/dist/src/bootstrap/initialise.js +434 -0
- package/dist/src/bootstrap/paths.js +15 -0
- package/dist/src/bootstrap/projection-runtime.js +27 -0
- package/dist/src/bootstrap/runner-quota-reporter.js +21 -0
- package/dist/src/bootstrap/runner-registry.js +30 -0
- package/dist/src/bootstrap/schedule-checkpoint-store.js +49 -0
- package/dist/src/bootstrap/self-update-application.js +195 -0
- package/dist/src/bootstrap/self-update-failure-log.js +33 -0
- package/dist/src/bootstrap/source-update-port.js +47 -0
- package/dist/src/bootstrap/status-publish-activity.js +42 -0
- package/dist/src/bootstrap/surface-api-applications.js +325 -0
- package/dist/src/bootstrap/surface-api-execution-applications.js +95 -0
- package/dist/src/bootstrap/surface-api-metadata.js +12 -0
- package/dist/src/bootstrap/surface-api-projection-pages.js +22 -0
- package/dist/src/bootstrap/surface-api-run-context.js +5 -0
- package/dist/src/bootstrap/surface-api-transcripts.js +57 -0
- package/dist/src/bootstrap/surface-api-work-applications.js +199 -0
- package/dist/src/bootstrap/surface-applications.js +7 -0
- package/dist/src/bootstrap/surface-cli-applications.js +701 -0
- package/dist/src/bootstrap/update-ledger.js +61 -0
- package/dist/src/bootstrap/update-maintenance-lease.js +176 -0
- package/dist/src/{version.js → bootstrap/version.js} +35 -51
- package/dist/src/control-plane/application/advance-once.js +184 -0
- package/dist/src/control-plane/application/control-plane-projection.js +42 -0
- package/dist/src/control-plane/application/control-plane-service.js +42 -0
- package/dist/src/control-plane/application/intake-pipeline.js +29 -0
- package/dist/src/control-plane/application/runner-control-service.js +66 -0
- package/dist/src/control-plane/application/runner-pipeline.js +57 -0
- package/dist/src/control-plane/application/schedule-service.js +47 -0
- package/dist/src/control-plane/application/work-cancellation-policy.js +31 -0
- package/dist/src/control-plane/contracts/commands.js +10 -0
- package/dist/src/control-plane/contracts/config.js +22 -0
- package/dist/src/control-plane/contracts/events.js +81 -0
- package/dist/src/control-plane/contracts/streams.js +5 -0
- package/dist/src/control-plane/domain/dispatch-policy.js +15 -0
- package/dist/src/control-plane/domain/quota-policy.js +26 -0
- package/dist/src/control-plane/domain/runner-quota.js +35 -0
- package/dist/src/control-plane/domain/schedule-policy.js +46 -0
- package/dist/src/control-plane/index.js +20 -0
- package/dist/src/control-plane/infrastructure/intake-host.js +24 -0
- package/dist/src/control-plane/infrastructure/resident-host.js +45 -0
- package/dist/src/control-plane/infrastructure/tick-host.js +35 -0
- package/dist/src/execution/application/activation-claim.js +50 -0
- package/dist/src/execution/application/active-run-cancellation.js +14 -0
- package/dist/src/execution/application/execution-activity.js +97 -0
- package/dist/src/execution/application/execution-projection.js +17 -0
- package/dist/src/execution/application/execution-service.js +372 -0
- package/dist/src/execution/application/execution-validation.js +26 -0
- package/dist/src/execution/application/recovery-service.js +212 -0
- package/dist/src/execution/application/run-lifecycle.js +115 -0
- package/dist/src/execution/application/run-liveness-service.js +80 -0
- package/dist/src/execution/application/run-repository.js +27 -0
- package/dist/src/execution/contracts/commands.js +1 -0
- package/dist/src/execution/contracts/config.js +35 -0
- package/dist/src/execution/contracts/control-plane.js +1 -0
- package/dist/src/execution/contracts/event-factory.js +53 -0
- package/dist/src/execution/contracts/events.js +274 -0
- package/dist/src/execution/contracts/identifiers.js +5 -0
- package/dist/src/execution/contracts/liveness.js +1 -0
- package/dist/src/execution/contracts/results.js +1 -0
- package/dist/src/execution/contracts/runner.js +15 -0
- package/dist/src/execution/contracts/streams.js +10 -0
- package/dist/src/execution/contracts/views.js +1 -0
- package/dist/src/execution/contracts/vocabulary.js +43 -0
- package/dist/src/execution/contracts/workspace.js +1 -0
- package/dist/src/execution/domain/run-result.js +34 -0
- package/dist/src/execution/domain/run.js +142 -0
- package/dist/src/execution/index.js +31 -0
- package/dist/src/execution/infrastructure/agent-runner-adapter.js +114 -0
- package/dist/src/execution/infrastructure/process-execution.js +29 -0
- package/dist/src/execution/infrastructure/prompt-templates.js +42 -0
- package/dist/src/execution/infrastructure/runners/claude.js +120 -0
- package/dist/src/execution/infrastructure/runners/codex.js +143 -0
- package/dist/src/execution/infrastructure/runners/command.js +4 -0
- package/dist/src/execution/infrastructure/runners/cursor.js +69 -0
- package/dist/src/execution/infrastructure/runners/fake-scenarios.js +99 -0
- package/dist/src/execution/infrastructure/runners/fake.js +65 -0
- package/dist/src/execution/infrastructure/runners/registry.js +26 -0
- package/dist/src/execution/infrastructure/transcript-store.js +223 -0
- package/dist/src/execution/infrastructure/transcripts.js +11 -0
- package/dist/src/execution/infrastructure/workspace/fake-workspace.js +19 -0
- package/dist/src/execution/infrastructure/workspace/git-workspace.js +241 -0
- package/dist/src/integrations/application/agent-run-publication-reactor.js +111 -0
- package/dist/src/integrations/application/artifact-registration-reactor.js +159 -0
- package/dist/src/integrations/application/poll-service.js +25 -0
- package/dist/src/integrations/application/terminal-agent-run-report.js +40 -0
- package/dist/src/integrations/application/work-admission.js +31 -0
- package/dist/src/integrations/application/work-conclusion.js +19 -0
- package/dist/src/integrations/contracts/artifact-events.js +60 -0
- package/dist/src/integrations/contracts/artifact-vocabulary.js +9 -0
- package/dist/src/integrations/contracts/config.js +14 -0
- package/dist/src/integrations/contracts/identifiers.js +5 -0
- package/dist/src/integrations/contracts/intake-rules.js +14 -0
- package/dist/src/integrations/contracts/intake.js +1 -0
- package/dist/src/integrations/contracts/outcome-vocabulary.js +7 -0
- package/dist/src/integrations/contracts/provider.js +37 -0
- package/dist/src/integrations/contracts/streams.js +10 -0
- package/dist/src/integrations/delivery/application/delivery-outcome-reactor.js +75 -0
- package/dist/src/integrations/delivery/application/delivery-projector.js +165 -0
- package/dist/src/integrations/delivery/application/delivery-service.js +173 -0
- package/dist/src/integrations/delivery/contracts/commands.js +1 -0
- package/dist/src/integrations/delivery/contracts/config.js +1 -0
- package/dist/src/integrations/delivery/contracts/event-factory.js +18 -0
- package/dist/src/integrations/delivery/contracts/events.js +86 -0
- package/dist/src/integrations/delivery/contracts/intents.js +89 -0
- package/dist/src/integrations/delivery/contracts/views.js +1 -0
- package/dist/src/integrations/delivery/contracts/vocabulary.js +22 -0
- package/dist/src/integrations/fake/durable-delivery-provider.js +48 -0
- package/dist/src/integrations/fake/external-sink.js +9 -0
- package/dist/src/integrations/fake/external-source.js +44 -0
- package/dist/src/integrations/fake/inbound-translator.js +130 -0
- package/dist/src/integrations/fake/provider.js +95 -0
- package/dist/src/integrations/github/application/agent-context-reader.js +54 -0
- package/dist/src/integrations/github/application/agent-run-comment.js +94 -0
- package/dist/src/integrations/github/application/comment-history-reader.js +43 -0
- package/dist/src/integrations/github/application/inbound-context.js +10 -0
- package/dist/src/integrations/github/application/inbound-review-signals.js +159 -0
- package/dist/src/integrations/github/application/inbound-translator.js +231 -0
- package/dist/src/integrations/github/application/inbound-watch-gate-signals.js +84 -0
- package/dist/src/integrations/github/application/intake-policy.js +23 -0
- package/dist/src/integrations/github/application/outbound-translator.js +47 -0
- package/dist/src/integrations/github/application/poll-service.js +29 -0
- package/dist/src/integrations/github/application/pull-request-translation.js +10 -0
- package/dist/src/integrations/github/application/resource-links.js +8 -0
- package/dist/src/integrations/github/application/review-command-translator.js +16 -0
- package/dist/src/integrations/github/application/wake-labels.js +95 -0
- package/dist/src/integrations/github/contracts/config.js +47 -0
- package/dist/src/integrations/github/contracts/events.js +127 -0
- package/dist/src/integrations/github/contracts/external-key.js +19 -0
- package/dist/src/integrations/github/contracts/payloads.js +9 -0
- package/dist/src/integrations/github/contracts/vocabulary.js +55 -0
- package/dist/src/integrations/github/index.js +31 -0
- package/dist/src/integrations/github/infrastructure/client-reads.js +214 -0
- package/dist/src/integrations/github/infrastructure/client.js +132 -0
- package/dist/src/integrations/github/infrastructure/content-fingerprint.js +10 -0
- package/dist/src/integrations/github/infrastructure/delivery.js +31 -0
- package/dist/src/integrations/github/infrastructure/etag-cache.js +56 -0
- package/dist/src/integrations/github/infrastructure/gh-auth.js +19 -0
- package/dist/src/integrations/github/infrastructure/issue-source.js +109 -0
- package/dist/src/integrations/github/infrastructure/pr-source.js +186 -0
- package/dist/src/integrations/github/infrastructure/review-source.js +70 -0
- package/dist/src/integrations/github/infrastructure/source.js +108 -0
- package/dist/src/integrations/github/provider.js +96 -0
- package/dist/src/integrations/index.js +30 -0
- package/dist/src/kernel/contracts/checkpoint-store.js +1 -0
- package/dist/src/kernel/contracts/clock.js +1 -0
- package/dist/src/kernel/contracts/commands.js +1 -0
- package/dist/src/kernel/contracts/event-journal.js +2 -0
- package/dist/src/kernel/contracts/event-schema.js +54 -0
- package/dist/src/kernel/contracts/events.js +11 -0
- package/dist/src/kernel/contracts/id-generator.js +1 -0
- package/dist/src/kernel/contracts/identifiers.js +8 -0
- package/dist/src/kernel/contracts/projection-store.js +1 -0
- package/dist/src/kernel/contracts/relations.js +1 -0
- package/dist/src/kernel/contracts/schema.js +17 -0
- package/dist/src/kernel/contracts/vocabulary.js +6 -0
- package/dist/src/kernel/domain/event-envelope.js +29 -0
- package/dist/src/kernel/domain/match-mode.js +11 -0
- package/dist/src/kernel/domain/relation.js +10 -0
- package/dist/src/kernel/index.js +17 -0
- package/dist/src/{lib/clock.js → kernel/infrastructure/system-clock.js} +3 -3
- package/dist/src/kernel/infrastructure/ulid-id-generator.js +9 -0
- package/dist/src/main.js +137 -1030
- package/dist/src/orchestration/application/accept-activity-outcome.js +25 -0
- package/dist/src/orchestration/application/accept-signal.js +35 -0
- package/dist/src/orchestration/application/advance-workflow.js +160 -0
- package/dist/src/orchestration/application/coordination-claims.js +107 -0
- package/dist/src/orchestration/application/group-budget-recorder.js +43 -0
- package/dist/src/orchestration/application/orchestration-projection.js +20 -0
- package/dist/src/orchestration/application/orchestration-repository.js +36 -0
- package/dist/src/orchestration/application/orchestration-service.js +82 -0
- package/dist/src/orchestration/application/request-child.js +118 -0
- package/dist/src/orchestration/application/signal-reactor.js +17 -0
- package/dist/src/orchestration/application/start-workflow.js +51 -0
- package/dist/src/orchestration/application/watch-reactor.js +82 -0
- package/dist/src/orchestration/contracts/activity-outcome.js +18 -0
- package/dist/src/orchestration/contracts/commands.js +1 -0
- package/dist/src/orchestration/contracts/config.js +128 -0
- package/dist/src/orchestration/contracts/event-decoder.js +263 -0
- package/dist/src/orchestration/contracts/events.js +41 -0
- package/dist/src/orchestration/contracts/identifiers.js +35 -0
- package/dist/src/orchestration/contracts/streams.js +38 -0
- package/dist/src/orchestration/contracts/views.js +1 -0
- package/dist/src/orchestration/contracts/vocabulary.js +30 -0
- package/dist/src/orchestration/domain/activation-policy.js +55 -0
- package/dist/src/orchestration/domain/approval-defaults.js +17 -0
- package/dist/src/orchestration/domain/child-policy.js +63 -0
- package/dist/src/orchestration/domain/compiler.js +162 -0
- package/dist/src/orchestration/domain/coordination-events.js +25 -0
- package/dist/src/orchestration/domain/decision-events.js +47 -0
- package/dist/src/orchestration/domain/interpreter.js +49 -0
- package/dist/src/orchestration/domain/operator-retry-policy.js +34 -0
- package/dist/src/orchestration/domain/retry-policy.js +26 -0
- package/dist/src/orchestration/domain/signal-policy.js +120 -0
- package/dist/src/orchestration/domain/supplemental-policy.js +61 -0
- package/dist/src/orchestration/domain/transition.js +55 -0
- package/dist/src/orchestration/domain/workflow-graph.js +41 -0
- package/dist/src/orchestration/domain/workflow-instance-events.js +236 -0
- package/dist/src/orchestration/domain/workflow-instance.js +41 -0
- package/dist/src/orchestration/domain/workflow-selector.js +21 -0
- package/dist/src/orchestration/index.js +19 -0
- package/dist/src/persistence/application/projection-runner.js +79 -0
- package/dist/src/persistence/filesystem/file-checkpoint-store.js +35 -0
- package/dist/src/persistence/filesystem/file-event-journal.js +143 -0
- package/dist/src/persistence/filesystem/file-lock.js +91 -0
- package/dist/src/persistence/filesystem/file-projection-store.js +64 -0
- package/dist/src/persistence/index.js +8 -0
- package/dist/src/persistence/memory/in-memory-checkpoint-store.js +14 -0
- package/dist/src/persistence/memory/in-memory-event-journal.js +88 -0
- package/dist/src/persistence/memory/in-memory-projection-store.js +17 -0
- package/dist/src/resources/application/lookup-projections.js +48 -0
- package/dist/src/resources/application/resource-lookup.js +16 -0
- package/dist/src/resources/application/resource-projections.js +81 -0
- package/dist/src/resources/application/resource-repository.js +18 -0
- package/dist/src/resources/application/resource-service.js +83 -0
- package/dist/src/resources/contracts/commands.js +1 -0
- package/dist/src/resources/contracts/config.js +2 -0
- package/dist/src/resources/contracts/events.js +83 -0
- package/dist/src/resources/contracts/identifiers.js +15 -0
- package/dist/src/resources/contracts/streams.js +6 -0
- package/dist/src/resources/contracts/views.js +1 -0
- package/dist/src/resources/contracts/vocabulary.js +67 -0
- package/dist/src/resources/domain/correlation.js +8 -0
- package/dist/src/resources/domain/resource.js +58 -0
- package/dist/src/resources/index.js +14 -0
- package/dist/src/surfaces/api/contracts/activities.js +1 -0
- package/dist/src/surfaces/api/contracts/board.js +2 -0
- package/dist/src/surfaces/api/contracts/common.js +46 -0
- package/dist/src/surfaces/api/contracts/control-plane.js +5 -0
- package/dist/src/surfaces/api/contracts/events.js +1 -0
- package/dist/src/surfaces/api/contracts/execution.js +1 -0
- package/dist/src/surfaces/api/contracts/index.js +13 -0
- package/dist/src/surfaces/api/contracts/observability.js +1 -0
- package/dist/src/surfaces/api/contracts/orchestration.js +1 -0
- package/dist/src/surfaces/api/contracts/resources.js +1 -0
- package/dist/src/surfaces/api/contracts/status.js +1 -0
- package/dist/src/surfaces/api/contracts/system.js +1 -0
- package/dist/src/surfaces/api/contracts/transport-values.js +39 -0
- package/dist/src/surfaces/api/contracts/work.js +17 -0
- package/dist/src/surfaces/api/http-server.js +121 -0
- package/dist/src/surfaces/api/presenters/activities.js +1 -0
- package/dist/src/surfaces/api/presenters/board.js +3 -0
- package/dist/src/surfaces/api/presenters/control-plane.js +1 -0
- package/dist/src/surfaces/api/presenters/events.js +1 -0
- package/dist/src/surfaces/api/presenters/execution.js +59 -0
- package/dist/src/surfaces/api/presenters/observability.js +1 -0
- package/dist/src/surfaces/api/presenters/orchestration.js +19 -0
- package/dist/src/surfaces/api/presenters/resources.js +15 -0
- package/dist/src/surfaces/api/presenters/status.js +3 -0
- package/dist/src/surfaces/api/presenters/system.js +14 -0
- package/dist/src/surfaces/api/presenters/work.js +16 -0
- package/dist/src/surfaces/api/problem-details.js +9 -0
- package/dist/src/surfaces/api/router.js +13 -0
- package/dist/src/surfaces/api/routes/activities.js +1 -0
- package/dist/src/surfaces/api/routes/applications.js +1 -0
- package/dist/src/surfaces/api/routes/commands.js +82 -0
- package/dist/src/surfaces/api/routes/control-plane.js +6 -0
- package/dist/src/surfaces/api/routes/events.js +1 -0
- package/dist/src/surfaces/api/routes/execution.js +8 -0
- package/dist/src/surfaces/api/routes/index.js +18 -0
- package/dist/src/surfaces/api/routes/observability.js +1 -0
- package/dist/src/surfaces/api/routes/orchestration.js +1 -0
- package/dist/src/surfaces/api/routes/pagination.js +78 -0
- package/dist/src/surfaces/api/routes/read.js +135 -0
- package/dist/src/surfaces/api/routes/resources.js +1 -0
- package/dist/src/surfaces/api/routes/responses.js +67 -0
- package/dist/src/surfaces/api/routes/system.js +1 -0
- package/dist/src/surfaces/api/routes/work.js +9 -0
- package/dist/src/surfaces/cli/commands/api.js +1 -0
- package/dist/src/surfaces/cli/commands/audit.js +10 -0
- package/dist/src/surfaces/cli/commands/correlate.js +2 -0
- package/dist/src/surfaces/cli/commands/doctor.js +6 -0
- package/dist/src/surfaces/cli/commands/init.js +39 -0
- package/dist/src/surfaces/cli/commands/sandbox-entrypoint.js +33 -0
- package/dist/src/surfaces/cli/commands/sandbox-setup.js +29 -0
- package/dist/src/surfaces/cli/commands/sandbox.js +105 -0
- package/dist/src/surfaces/cli/commands/self-update.js +38 -0
- package/dist/src/surfaces/cli/commands/smoke.js +16 -0
- package/dist/src/surfaces/cli/commands/start.js +1 -0
- package/dist/src/surfaces/cli/commands/stop.js +13 -0
- package/dist/src/surfaces/cli/commands/tick.js +1 -0
- package/dist/src/surfaces/cli/commands/ui.js +2 -0
- package/dist/src/surfaces/cli/commands/validate-state.js +5 -0
- package/dist/src/surfaces/cli/infrastructure/docker-cli.js +221 -0
- package/dist/src/surfaces/cli/infrastructure/process-log.js +86 -0
- package/dist/src/surfaces/cli/main.js +140 -0
- package/dist/src/surfaces/cli/usage.js +33 -0
- package/dist/src/surfaces/contracts/config.js +34 -0
- package/dist/src/surfaces/index.js +47 -0
- package/dist/src/surfaces/web-assets/assets/index-Bfn4FwZG.css +1 -0
- package/dist/src/surfaces/web-assets/assets/index-ipB6JPGU.js +1090 -0
- package/dist/src/surfaces/web-assets/index.html +13 -0
- package/dist/src/surfaces/web-host/asset-source.js +1 -0
- package/dist/src/surfaces/web-host/packaged-assets.js +44 -0
- package/dist/src/work/application/work-projection.js +59 -0
- package/dist/src/work/application/work-repository.js +18 -0
- package/dist/src/work/application/work-service.js +117 -0
- package/dist/src/work/contracts/commands.js +1 -0
- package/dist/src/work/contracts/config.js +2 -0
- package/dist/src/work/contracts/events.js +103 -0
- package/dist/src/work/contracts/identifiers.js +5 -0
- package/dist/src/work/contracts/streams.js +6 -0
- package/dist/src/work/contracts/views.js +1 -0
- package/dist/src/work/contracts/vocabulary.js +6 -0
- package/dist/src/work/domain/work-item.js +89 -0
- package/dist/src/work/index.js +11 -0
- package/docker/Dockerfile +10 -6
- package/docker/Dockerfile.packaged +3 -3
- package/package.json +29 -4
- package/prompts/implement.md +2 -2
- package/templates/SETUP.md +7 -7
- package/dist/src/adapters/claude/claude-runner.js +0 -529
- package/dist/src/adapters/codex/codex-runner.js +0 -503
- package/dist/src/adapters/cursor/cursor-runner.js +0 -418
- package/dist/src/adapters/docker/docker-cli.js +0 -150
- package/dist/src/adapters/fake/fake-artifact-verifier.js +0 -14
- package/dist/src/adapters/fake/fake-github-pull-request-activity-source.js +0 -77
- package/dist/src/adapters/fake/fake-resource-index.js +0 -21
- package/dist/src/adapters/fake/fake-runner.js +0 -77
- package/dist/src/adapters/fake/fake-ticketing-system.js +0 -185
- package/dist/src/adapters/fake/fake-workspace-manager.js +0 -81
- package/dist/src/adapters/fs/resource-index.js +0 -126
- package/dist/src/adapters/fs/self-update-ledger.js +0 -17
- package/dist/src/adapters/fs/state-store.js +0 -775
- package/dist/src/adapters/git/git-workspace-manager.js +0 -289
- package/dist/src/adapters/github/github-artifact-verifier.js +0 -38
- package/dist/src/adapters/github/github-auth.js +0 -16
- package/dist/src/adapters/github/github-client.js +0 -249
- package/dist/src/adapters/github/github-etag-cache.js +0 -57
- package/dist/src/adapters/github/github-issues-work-source.js +0 -692
- package/dist/src/adapters/github/github-pull-request-activity-source.js +0 -584
- package/dist/src/adapters/github/github-pull-request-merge-actor.js +0 -45
- package/dist/src/adapters/http/ui-assets.js +0 -1188
- package/dist/src/adapters/http/ui-data.js +0 -1107
- package/dist/src/adapters/http/ui-server.js +0 -458
- package/dist/src/adapters/runner/cli-command.js +0 -73
- package/dist/src/adapters/runner/prompt-templates.js +0 -76
- package/dist/src/adapters/runner/runner-cli-adapter.js +0 -112
- package/dist/src/adapters/runner/runner-registry.js +0 -139
- package/dist/src/adapters/runner/runtime-events.js +0 -32
- package/dist/src/adapters/runner/stage-prompt.js +0 -268
- package/dist/src/adapters/runner/transcripts.js +0 -25
- package/dist/src/cli/audit-command.js +0 -36
- package/dist/src/cli/correlate-command.js +0 -62
- package/dist/src/cli/doctor-command.js +0 -48
- package/dist/src/cli/init-command.js +0 -18
- package/dist/src/cli/sandbox-command.js +0 -258
- package/dist/src/cli/sandbox-entrypoint-command.js +0 -88
- package/dist/src/cli/sandbox-exec-logging.js +0 -7
- package/dist/src/cli/sandbox-resume.js +0 -79
- package/dist/src/cli/sandbox-setup-command.js +0 -31
- package/dist/src/cli/scaffold-assets.js +0 -100
- package/dist/src/cli/self-update-command.js +0 -198
- package/dist/src/cli/startup-preflight.js +0 -134
- package/dist/src/cli/stop-command.js +0 -46
- package/dist/src/cli/ui-command.js +0 -27
- package/dist/src/config/defaults.js +0 -10
- package/dist/src/config/discover-config-files.js +0 -11
- package/dist/src/config/load-config.js +0 -69
- package/dist/src/config/split-config.js +0 -14
- package/dist/src/core/active-run-recovery.js +0 -44
- package/dist/src/core/approval-intents.js +0 -16
- package/dist/src/core/audit-events.js +0 -82
- package/dist/src/core/control-plane.js +0 -192
- package/dist/src/core/event-builders.js +0 -153
- package/dist/src/core/event-resolver.js +0 -249
- package/dist/src/core/lifecycle-service.js +0 -8
- package/dist/src/core/live-execution.js +0 -81
- package/dist/src/core/outbox.js +0 -198
- package/dist/src/core/policy-engine.js +0 -389
- package/dist/src/core/projection-updater.js +0 -693
- package/dist/src/core/quota-backoff.js +0 -69
- package/dist/src/core/run-lease.js +0 -38
- package/dist/src/core/scheduled-workflow-source.js +0 -199
- package/dist/src/core/sink-router.js +0 -107
- package/dist/src/core/stale-run-reconciler.js +0 -352
- package/dist/src/core/tick-runner.js +0 -2442
- package/dist/src/core/workspace-cleanup.js +0 -117
- package/dist/src/domain/branch-naming.js +0 -14
- package/dist/src/domain/custom-commands.js +0 -58
- package/dist/src/domain/event-types.js +0 -158
- package/dist/src/domain/manual-labels.js +0 -1
- package/dist/src/domain/resource-uri.js +0 -38
- package/dist/src/domain/runner-routing.js +0 -127
- package/dist/src/domain/runtime-events.js +0 -36
- package/dist/src/domain/schema.js +0 -1233
- package/dist/src/domain/sources.js +0 -16
- package/dist/src/domain/stages.js +0 -36
- package/dist/src/domain/work-item-labels.js +0 -48
- package/dist/src/domain/work-item-lifecycle.js +0 -10
- package/dist/src/domain/work-item-status.js +0 -33
- package/dist/src/domain/workflows.js +0 -159
- package/dist/src/lib/deep-merge.js +0 -14
- package/dist/src/lib/detached-process-logging.js +0 -46
- package/dist/src/lib/event-log.js +0 -21
- package/dist/src/lib/format.js +0 -41
- package/dist/src/lib/json-file.js +0 -23
- package/dist/src/lib/lock.js +0 -181
- package/dist/src/lib/log-rotation.js +0 -42
- package/dist/src/lib/paths.js +0 -52
- package/dist/src/lib/process-identity.js +0 -54
- package/dist/src/lib/state-health.js +0 -30
- package/dist/src/lib/work-id.js +0 -19
- package/dist/src/lib/yaml-file.js +0 -20
- /package/dist/src/{core → activities/pr}/contracts.js +0 -0
- /package/dist/src/{domain/types.js → control-plane/contracts/views.js} +0 -0
|
@@ -1,1188 +0,0 @@
|
|
|
1
|
-
import { wakeVersion } from '../../version.js';
|
|
2
|
-
const logoSvg = `<svg viewBox="0 0 110 110" xmlns="http://www.w3.org/2000/svg">
|
|
3
|
-
<g transform="translate(55 55)">
|
|
4
|
-
<circle r="54" fill="none" stroke="#0F766E" opacity=".10"></circle>
|
|
5
|
-
<path d="M-42.5-11.5 A44 44 0 1 0 42.5-11.5" fill="none" stroke="#2DD4BF" stroke-width="7.5" stroke-linecap="round"></path>
|
|
6
|
-
<path d="M-36-25.2 A44 44 0 0 1-25.2-36" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
|
|
7
|
-
<path d="M25.2-36 A44 44 0 0 1 36-25.2" fill="none" stroke="#5EEAD4" stroke-width="7.5" stroke-linecap="round"></path>
|
|
8
|
-
<circle r="25" fill="#2DD4BF" opacity=".08"></circle>
|
|
9
|
-
<circle r="11" fill="#2DD4BF" opacity=".25"></circle>
|
|
10
|
-
<path d="M-18 23 C-9 22 -8 2 0-2 C8 2 9 22 18 23" fill="none" stroke="#2DD4BF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"></path>
|
|
11
|
-
</g>
|
|
12
|
-
</svg>`;
|
|
13
|
-
const faviconHref = `data:image/svg+xml,${encodeURIComponent(logoSvg)}`;
|
|
14
|
-
export const indexHtml = `<!DOCTYPE html>
|
|
15
|
-
<html lang="en">
|
|
16
|
-
<head>
|
|
17
|
-
<meta charset="utf-8" />
|
|
18
|
-
<title>Wake control plane</title>
|
|
19
|
-
<link rel="icon" type="image/svg+xml" href="${faviconHref}" />
|
|
20
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
21
|
-
<style>
|
|
22
|
-
:root {
|
|
23
|
-
color-scheme: dark light;
|
|
24
|
-
--brand: #0f766e;
|
|
25
|
-
--brand-dark: #134e4a;
|
|
26
|
-
--brand-darker: #103a37;
|
|
27
|
-
--accent: #2dd4bf;
|
|
28
|
-
--accent-light: #5eead4;
|
|
29
|
-
}
|
|
30
|
-
body { font-family: -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; background: #14161a; color: #e8e8e8; }
|
|
31
|
-
.topbar { display: flex; align-items: center; gap: 0.55rem; padding: 0.55rem 1rem; background: var(--brand); }
|
|
32
|
-
.topbar .logo { display: flex; width: 24px; height: 24px; flex-shrink: 0; }
|
|
33
|
-
.topbar .logo svg { width: 100%; height: 100%; display: block; }
|
|
34
|
-
.topbar .brand-name { font-size: 1.05rem; font-weight: 700; color: #fff; letter-spacing: 0.01em; }
|
|
35
|
-
.topbar .version { color: rgba(255, 255, 255, 0.7); font-size: 0.78rem; }
|
|
36
|
-
.statusbar { display: flex; align-items: center; gap: 1rem; padding: 0.45rem 1rem; background: var(--brand-dark); border-top: 1px solid rgba(0, 0, 0, 0.18); flex-wrap: wrap; font-size: 0.8rem; }
|
|
37
|
-
.statusbar .meta { color: rgba(255, 255, 255, 0.72); }
|
|
38
|
-
.statusbar button { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.22rem 0.55rem; cursor: pointer; font-size: 0.78rem; }
|
|
39
|
-
.statusbar button:hover:not(:disabled) { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
|
|
40
|
-
.statusbar button:disabled { cursor: wait; opacity: 0.62; }
|
|
41
|
-
.statusbar .pause-control { background: #5c1f1a; border-color: #ff8f7f; font-weight: 700; }
|
|
42
|
-
.statusbar .pause-control:hover:not(:disabled) { background: #733027; border-color: #ffd0c8; }
|
|
43
|
-
.statusbar .pause-control.is-paused { background: #1f4b34; border-color: #7fe3a3; }
|
|
44
|
-
.statusbar .pause-control.is-paused:hover:not(:disabled) { background: #276340; border-color: #c3f3d2; }
|
|
45
|
-
.pill { padding: 0.15rem 0.5rem; border-radius: 999px; font-size: 0.75rem; font-weight: 600; }
|
|
46
|
-
.pill-idle { background: #1f3d2c; color: #7fe3a3; }
|
|
47
|
-
.pill-polling { background: #1f3350; color: #7fb3ff; }
|
|
48
|
-
.pill-working { background: #2d1f50; color: #c79bff; }
|
|
49
|
-
.pill-paused { background: #4a3510; color: #ffcf7f; }
|
|
50
|
-
.pill-ready { background: #1f3d2c; color: #7fe3a3; }
|
|
51
|
-
.pill-active { background: #2d1f50; color: #c79bff; }
|
|
52
|
-
.pill-scheduled { background: #1f3350; color: #7fb3ff; }
|
|
53
|
-
.pill-needs-human { background: #4a3510; color: #ffcf7f; }
|
|
54
|
-
.pill-error { background: #3d1f1f; color: #ff8f7f; }
|
|
55
|
-
.pill-finished { background: #252830; color: #9aa2ad; }
|
|
56
|
-
.pill-frozen { background: #102a43; color: #9bd8ff; border: 1px solid #38bdf8; }
|
|
57
|
-
nav { display: flex; gap: 0.25rem; padding: 0.4rem 1rem 0 0.3rem; background: var(--brand-darker); border-bottom: 1px solid #2c313a; }
|
|
58
|
-
nav button { background: none; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.65); padding: 0.4rem 0.7rem 0.45rem; margin-bottom: -1px; cursor: pointer; font-size: 0.85rem; transition: color 0.12s ease; }
|
|
59
|
-
nav button:hover { color: #fff; }
|
|
60
|
-
nav button.active { color: var(--accent-light); border-bottom-color: var(--accent); }
|
|
61
|
-
main { padding: 1rem; }
|
|
62
|
-
.columns { display: grid; grid-template-columns: repeat(6, minmax(180px, 1fr)); gap: 0.6rem; overflow-x: auto; }
|
|
63
|
-
.col { background: #1a1d23; border-radius: 10px; padding: 0.5rem; min-height: 200px; }
|
|
64
|
-
.col-header { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; margin: 0.2rem 0.4rem 0.5rem; }
|
|
65
|
-
.col h2 { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.04em; color: #9aa2ad; margin: 0; }
|
|
66
|
-
.col-toggle { display: none; width: 1.65rem; height: 1.65rem; flex: 0 0 auto; align-items: center; justify-content: center; background: transparent; border: 1px solid #3a4150; border-radius: 6px; color: #cbd5e1; cursor: pointer; font-size: 1rem; line-height: 1; }
|
|
67
|
-
.col-toggle:hover { border-color: var(--accent); color: var(--accent-light); }
|
|
68
|
-
.card { background: #22262e; border: 1px solid #2c313a; border-radius: 8px; padding: 0.5rem; margin-bottom: 0.5rem; cursor: pointer; font-size: 0.8rem; transition: border-color 0.12s ease; }
|
|
69
|
-
.card:hover { border-color: var(--accent); }
|
|
70
|
-
.card .title { font-weight: 600; margin-bottom: 0.25rem; }
|
|
71
|
-
.card .meta { color: #9aa2ad; font-size: 0.72rem; }
|
|
72
|
-
.item-summary { margin: 0.45rem 0 0.75rem; }
|
|
73
|
-
.item-summary .meta { color: #9aa2ad; font-size: 0.78rem; }
|
|
74
|
-
.item-summary .card-stats { font-size: 0.76rem; }
|
|
75
|
-
.item-summary .child-run { max-width: 560px; }
|
|
76
|
-
.detail-kv { display: grid; grid-template-columns: max-content 1fr; gap: 0.35rem 0.7rem; align-items: baseline; margin: 0.75rem 0 1rem; font-size: 0.8rem; }
|
|
77
|
-
.detail-kv dt { margin: 0; color: #9aa2ad; }
|
|
78
|
-
.detail-kv dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
|
|
79
|
-
.child-run { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 0.45rem; align-items: center; margin-top: 0.45rem; padding: 0.4rem; border-radius: 6px; background: #181c22; border: 1px solid #334155; color: #cbd5e1; font-size: 0.72rem; }
|
|
80
|
-
.child-run .dot { width: 0.48rem; height: 0.48rem; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.14); }
|
|
81
|
-
.child-run .dot-watch { background: #ffcf7f; box-shadow: 0 0 0 3px rgba(255, 207, 127, 0.14); }
|
|
82
|
-
.child-run .run-title { font-weight: 650; color: #e5e7eb; }
|
|
83
|
-
.child-run .run-meta { grid-column: 2; color: #94a3b8; }
|
|
84
|
-
.card-stats { color: #8892a0; font-size: 0.7rem; margin-top: 0.3rem; }
|
|
85
|
-
.card-ready { border-left: 3px solid #7fe3a3; }
|
|
86
|
-
.card-active { border-left: 3px solid #c79bff; }
|
|
87
|
-
.card-scheduled { border-left: 3px solid #7fb3ff; }
|
|
88
|
-
.card-needs-human { border-left: 3px solid #ffcf7f; }
|
|
89
|
-
.card-error { border-left: 3px solid #ff8f7f; }
|
|
90
|
-
.chip { display: inline-block; background: #2c313a; border-radius: 4px; padding: 0.05rem 0.35rem; font-size: 0.68rem; margin-right: 0.2rem; }
|
|
91
|
-
.chip-label { background: transparent; border: 1px solid #3a4150; color: #9aa2ad; margin-bottom: 0.2rem; }
|
|
92
|
-
.chip-meta { border: 1px solid #3a4150; color: #cbd5e1; margin-bottom: 0.2rem; }
|
|
93
|
-
table { border-collapse: collapse; width: 100%; font-size: 0.8rem; }
|
|
94
|
-
th, td { text-align: left; padding: 0.35rem 0.5rem; border-bottom: 1px solid #2c313a; }
|
|
95
|
-
th { color: #9aa2ad; font-weight: 600; }
|
|
96
|
-
pre { background: #1a1d23; padding: 0.75rem; border-radius: 6px; overflow: auto; font-size: 0.75rem; }
|
|
97
|
-
.modal-overlay { position: fixed; inset: 0; display: none; align-items: center; justify-content: center; background: rgba(0, 0, 0, 0.58); padding: 1.25rem; z-index: 10; }
|
|
98
|
-
.modal-overlay.open { display: flex; }
|
|
99
|
-
.modal { width: calc(100vw - 3rem); height: calc(100vh - 3rem); background: #191c22; border: 1px solid #2c313a; border-radius: 8px; display: flex; flex-direction: column; box-shadow: 0 18px 60px rgba(0, 0, 0, 0.38); }
|
|
100
|
-
.modal-header { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.85rem 1rem 0.55rem; border-bottom: 1px solid #2c313a; }
|
|
101
|
-
.modal-header h2 { margin: 0; font-size: 1rem; }
|
|
102
|
-
.modal .close { cursor: pointer; color: #9aa2ad; background: none; border: 0; font-size: 0.9rem; padding: 0.2rem 0; }
|
|
103
|
-
.modal .close:hover { color: var(--accent-light); }
|
|
104
|
-
.modal-tabs { display: flex; gap: 0.25rem; padding: 0 1rem; border-bottom: 1px solid #2c313a; }
|
|
105
|
-
.modal-tabs .nav-button { background: none; border: none; border-bottom: 2px solid transparent; color: rgba(255, 255, 255, 0.65); padding: 0.45rem 0.7rem 0.5rem; margin-bottom: -1px; cursor: pointer; font-size: 0.85rem; }
|
|
106
|
-
.modal-tabs .nav-button:hover { color: #fff; }
|
|
107
|
-
.modal-tabs .nav-button.active { color: var(--accent-light); border-bottom-color: var(--accent); }
|
|
108
|
-
.modal-body { flex: 1; min-height: 0; overflow-y: auto; padding: 1rem; }
|
|
109
|
-
.modal-body h3:first-child { margin-top: 0; }
|
|
110
|
-
.event-list { display: flex; flex-direction: column; gap: 0.45rem; }
|
|
111
|
-
.event-card { background: #20242c; border: 1px solid #2c313a; border-radius: 6px; overflow: hidden; }
|
|
112
|
-
.event-card:hover { border-color: #3a4150; }
|
|
113
|
-
.event-summary { display: grid; grid-template-columns: minmax(11rem, 14rem) minmax(10rem, 1fr) minmax(8rem, 18rem) 2.4rem; gap: 0.65rem; align-items: center; width: 100%; min-height: 2.25rem; background: transparent; border: 0; color: inherit; padding: 0.45rem 0.65rem; text-align: left; cursor: pointer; font: inherit; }
|
|
114
|
-
.event-summary:hover { background: rgba(255, 255, 255, 0.03); }
|
|
115
|
-
.event-time, .event-type, .event-id, .event-direction { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
116
|
-
.event-time, .event-id { color: #9aa2ad; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.72rem; }
|
|
117
|
-
.event-type { font-weight: 650; font-size: 0.8rem; }
|
|
118
|
-
.event-direction { justify-self: end; width: 1.7rem; height: 1.7rem; display: inline-flex; align-items: center; justify-content: center; border-radius: 999px; background: #151922; border: 1px solid #334155; color: var(--accent-light); font-size: 0.9rem; }
|
|
119
|
-
.event-json { display: none; position: relative; border-top: 1px solid #2c313a; background: #14171d; }
|
|
120
|
-
.event-card.expanded .event-json { display: block; }
|
|
121
|
-
.event-copy { position: absolute; top: 0.55rem; right: 0.55rem; z-index: 1; background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.16rem 0.48rem; cursor: pointer; font-size: 0.72rem; }
|
|
122
|
-
.event-copy:hover { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
|
|
123
|
-
.event-json pre { margin: 0; padding-top: 2.35rem; border-radius: 0; background: transparent; }
|
|
124
|
-
.transcript-session { margin: 0.75rem 0; color: #9aa2ad; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.75rem; text-align: center; }
|
|
125
|
-
.transcript-entry { background: #101216; border: 1px solid #2c313a; border-radius: 6px; margin-bottom: 0.75rem; overflow: hidden; }
|
|
126
|
-
.transcript-head { color: #9aa2ad; background: #171a20; border-bottom: 1px solid #2c313a; padding: 0.45rem 0.6rem; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.72rem; }
|
|
127
|
-
.transcript-text { white-space: pre-wrap; margin: 0; background: transparent; border-radius: 0; font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 0.76rem; }
|
|
128
|
-
@media (max-width: 767px) {
|
|
129
|
-
.modal-overlay { padding: 0; }
|
|
130
|
-
.modal { width: 100%; height: 100%; max-height: none; border-radius: 0; border-left: 0; border-right: 0; }
|
|
131
|
-
.columns { display: flex; flex-direction: column; overflow-x: unset; }
|
|
132
|
-
.col { min-height: unset; }
|
|
133
|
-
.col-toggle { display: inline-flex; }
|
|
134
|
-
.col.is-collapsed .col-cards { display: none; }
|
|
135
|
-
.col-empty { padding: 0.25rem 0.5rem; }
|
|
136
|
-
.col-empty .col-header { margin: 0.1rem 0.4rem; }
|
|
137
|
-
.event-summary { grid-template-columns: minmax(8.5rem, 10rem) minmax(8rem, 1fr) 2.2rem; gap: 0.5rem; }
|
|
138
|
-
.event-id { display: none; }
|
|
139
|
-
}
|
|
140
|
-
.tiles { display: flex; gap: 0.6rem; flex-wrap: wrap; margin-bottom: 1rem; }
|
|
141
|
-
.tile { background: #1a1d23; border-radius: 10px; padding: 0.6rem 0.9rem; min-width: 120px; }
|
|
142
|
-
.tile .n { font-size: 1.3rem; font-weight: 700; }
|
|
143
|
-
.tile .l { color: #9aa2ad; font-size: 0.72rem; text-transform: uppercase; }
|
|
144
|
-
.toolbar { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; margin-bottom: 1rem; }
|
|
145
|
-
.toolbar label { display: inline-flex; align-items: center; gap: 0.35rem; color: #9aa2ad; font-size: 0.8rem; }
|
|
146
|
-
select { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; font-size: 0.8rem; }
|
|
147
|
-
select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.15); }
|
|
148
|
-
.metric-bar { width: 160px; height: 0.5rem; background: #101216; border-radius: 999px; overflow: hidden; }
|
|
149
|
-
.metric-bar-fill { height: 100%; background: var(--accent); border-radius: 999px; }
|
|
150
|
-
.stacked-bar { display: flex; width: min(520px, 45vw); min-width: 220px; height: 0.75rem; background: #101216; border-radius: 999px; overflow: hidden; }
|
|
151
|
-
.stacked-segment { height: 100%; min-width: 1px; }
|
|
152
|
-
.metric-table-value { display: flex; align-items: center; gap: 0.55rem; }
|
|
153
|
-
.amber { color: #ffcf7f; }
|
|
154
|
-
.red { color: #ff8f7f; }
|
|
155
|
-
.ok { color: #7fe3a3; }
|
|
156
|
-
input[type=text] { background: #1a1d23; border: 1px solid #2c313a; color: #e8e8e8; padding: 0.3rem 0.5rem; border-radius: 6px; margin-bottom: 0.6rem; width: 260px; transition: border-color 0.12s ease, box-shadow 0.12s ease; }
|
|
157
|
-
input[type=text]:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(45, 212, 191, 0.15); }
|
|
158
|
-
a { color: var(--accent-light); text-decoration: none; }
|
|
159
|
-
a:hover { text-decoration: underline; }
|
|
160
|
-
.resource-list { list-style: none; padding: 0; margin: 0 0 1rem; }
|
|
161
|
-
.resource-list li { display: flex; align-items: baseline; gap: 0.4rem; margin-bottom: 0.35rem; font-size: 0.8rem; }
|
|
162
|
-
.action-bar { display: flex; align-items: center; gap: 0.45rem; flex-wrap: wrap; margin: 0.6rem 0 0.2rem; }
|
|
163
|
-
.btn { background: rgba(255, 255, 255, 0.08); border: 1px solid rgba(255, 255, 255, 0.18); color: #fff; border-radius: 6px; padding: 0.22rem 0.55rem; cursor: pointer; font-size: 0.78rem; }
|
|
164
|
-
.btn:hover:not(:disabled) { border-color: var(--accent-light); background: rgba(45, 212, 191, 0.16); }
|
|
165
|
-
.btn.danger { background: #5c1f1a; border-color: #ff8f7f; }
|
|
166
|
-
.btn.danger:hover:not(:disabled) { background: #733027; border-color: #ffd0c8; }
|
|
167
|
-
.btn:disabled { cursor: default; opacity: 0.62; }
|
|
168
|
-
</style>
|
|
169
|
-
</head>
|
|
170
|
-
<body>
|
|
171
|
-
<header class="topbar">
|
|
172
|
-
<span class="logo">${logoSvg}</span>
|
|
173
|
-
<span class="brand-name">Wake</span>
|
|
174
|
-
<span class="version">${wakeVersion}</span>
|
|
175
|
-
</header>
|
|
176
|
-
<div class="statusbar">
|
|
177
|
-
<span id="loop-pill" class="pill">…</span>
|
|
178
|
-
<button id="pause-toggle" type="button" class="pause-control">Pause</button>
|
|
179
|
-
<button id="force-tick" type="button">Tick now</button>
|
|
180
|
-
<span id="status-summary" class="meta"></span>
|
|
181
|
-
</div>
|
|
182
|
-
<nav>
|
|
183
|
-
<button data-view="board" class="active">Board</button>
|
|
184
|
-
<button data-view="activity">Activity</button>
|
|
185
|
-
<button data-view="runs">Runs</button>
|
|
186
|
-
<button data-view="analytics">Analytics</button>
|
|
187
|
-
<button data-view="config">Config</button>
|
|
188
|
-
<button data-view="health">Health</button>
|
|
189
|
-
</nav>
|
|
190
|
-
<main id="main"></main>
|
|
191
|
-
<div id="modal-overlay" class="modal-overlay">
|
|
192
|
-
<section class="modal" role="dialog" aria-modal="true" aria-labelledby="modal-title">
|
|
193
|
-
<div class="modal-header">
|
|
194
|
-
<h2 id="modal-title"></h2>
|
|
195
|
-
<button type="button" class="close" id="modal-close">close ✕</button>
|
|
196
|
-
</div>
|
|
197
|
-
<div id="modal-tabs" class="modal-tabs"></div>
|
|
198
|
-
<div id="modal-body" class="modal-body"></div>
|
|
199
|
-
</section>
|
|
200
|
-
</div>
|
|
201
|
-
<script>
|
|
202
|
-
const API = '/api/v1';
|
|
203
|
-
const CONDITIONS = ['ready', 'scheduled', 'active', 'needs-human', 'error', 'finished'];
|
|
204
|
-
const COND_PILL = {
|
|
205
|
-
ready: 'pill-ready',
|
|
206
|
-
active: 'pill-active',
|
|
207
|
-
scheduled: 'pill-scheduled',
|
|
208
|
-
'needs-human': 'pill-needs-human',
|
|
209
|
-
error: 'pill-error',
|
|
210
|
-
finished: 'pill-finished',
|
|
211
|
-
};
|
|
212
|
-
const COND_CARD = {
|
|
213
|
-
ready: 'card-ready',
|
|
214
|
-
active: 'card-active',
|
|
215
|
-
scheduled: 'card-scheduled',
|
|
216
|
-
'needs-human': 'card-needs-human',
|
|
217
|
-
error: 'card-error',
|
|
218
|
-
};
|
|
219
|
-
const BOARD_COLLAPSE_STORAGE_KEY = 'wake:board:collapsed-columns';
|
|
220
|
-
let currentView = 'board';
|
|
221
|
-
let analyticsWindow = '1d';
|
|
222
|
-
let analyticsMetric = 'runs-over-time';
|
|
223
|
-
let activeViewRequest = null;
|
|
224
|
-
let activeViewRequestId = 0;
|
|
225
|
-
|
|
226
|
-
const METRIC_OPTIONS = [
|
|
227
|
-
['runs-over-time', 'Runs over time'],
|
|
228
|
-
['runs-by-status-over-time', 'Runs by status over time'],
|
|
229
|
-
['runs-by-status', 'Runs by status'],
|
|
230
|
-
['runs-by-action', 'Runs by action/stage'],
|
|
231
|
-
['runs-by-repo', 'Runs by repo'],
|
|
232
|
-
['runs-by-runner', 'Runs by runner'],
|
|
233
|
-
['runs-by-model', 'Runs by model'],
|
|
234
|
-
['runs-by-tier', 'Runs by tier'],
|
|
235
|
-
['tokens-over-time', 'Tokens over time'],
|
|
236
|
-
['tokens-by-action', 'Tokens by action/stage'],
|
|
237
|
-
['tokens-by-runner', 'Tokens by runner'],
|
|
238
|
-
['tokens-by-model', 'Tokens by model'],
|
|
239
|
-
['duration-by-action', 'Duration by action/stage'],
|
|
240
|
-
['duration-over-time', 'Duration over time'],
|
|
241
|
-
['work-items-over-time', 'Work items completed over time'],
|
|
242
|
-
['work-item-durations', 'Work item e2e duration'],
|
|
243
|
-
['work-item-totals', 'Work item totals'],
|
|
244
|
-
];
|
|
245
|
-
|
|
246
|
-
async function getJson(path, signal) {
|
|
247
|
-
const res = await fetch(API + path, signal ? { signal } : undefined);
|
|
248
|
-
if (!res.ok) throw new Error(path + ' -> ' + res.status);
|
|
249
|
-
return res.json();
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
async function postJson(path) {
|
|
253
|
-
const res = await fetch(API + path, { method: 'POST' });
|
|
254
|
-
if (!res.ok) throw new Error(path + ' -> ' + res.status);
|
|
255
|
-
return res.json();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
async function deleteJson(path) {
|
|
259
|
-
const res = await fetch(API + path, { method: 'DELETE' });
|
|
260
|
-
if (!res.ok) throw new Error(path + ' -> ' + res.status);
|
|
261
|
-
return res.json();
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function fmtMs(ms) {
|
|
265
|
-
if (ms === undefined || ms === null) return '—';
|
|
266
|
-
const s = Math.floor(ms / 1000);
|
|
267
|
-
if (s < 60) return s + 's';
|
|
268
|
-
const m = Math.floor(s / 60);
|
|
269
|
-
if (m < 60) return m + 'm';
|
|
270
|
-
const h = Math.floor(m / 60);
|
|
271
|
-
if (h < 24) return h + 'h';
|
|
272
|
-
return Math.floor(h / 24) + 'd';
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
function el(tag, attrs, children) {
|
|
276
|
-
const node = document.createElement(tag);
|
|
277
|
-
for (const [k, v] of Object.entries(attrs || {})) {
|
|
278
|
-
if (k === 'text') node.textContent = v;
|
|
279
|
-
else if (k.startsWith('on')) node.addEventListener(k.slice(2), v);
|
|
280
|
-
else node.setAttribute(k, v);
|
|
281
|
-
}
|
|
282
|
-
for (const child of children || []) node.appendChild(child);
|
|
283
|
-
return node;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
function isActiveRequest(requestId) {
|
|
287
|
-
return activeViewRequest && activeViewRequest.id === requestId;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function readCollapsedColumns() {
|
|
291
|
-
try {
|
|
292
|
-
const raw = window.localStorage.getItem(BOARD_COLLAPSE_STORAGE_KEY);
|
|
293
|
-
if (!raw) return new Set();
|
|
294
|
-
const values = JSON.parse(raw);
|
|
295
|
-
if (!Array.isArray(values)) return new Set();
|
|
296
|
-
return new Set(values.filter((value) => CONDITIONS.includes(value)));
|
|
297
|
-
} catch {
|
|
298
|
-
return new Set();
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
function writeCollapsedColumns(collapsed) {
|
|
303
|
-
try {
|
|
304
|
-
window.localStorage.setItem(BOARD_COLLAPSE_STORAGE_KEY, JSON.stringify([...collapsed]));
|
|
305
|
-
} catch {
|
|
306
|
-
// Ignore storage failures; the toggle should still work for this render.
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function setColumnCollapsed(column, button, collapsed) {
|
|
311
|
-
const label = collapsed ? 'Expand column' : 'Collapse column';
|
|
312
|
-
column.classList.toggle('is-collapsed', collapsed);
|
|
313
|
-
button.textContent = collapsed ? '+' : '-';
|
|
314
|
-
button.setAttribute('aria-label', label);
|
|
315
|
-
button.setAttribute('aria-expanded', String(!collapsed));
|
|
316
|
-
button.setAttribute('title', label);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function loadingNode(label) {
|
|
320
|
-
return el('p', { class: 'meta', text: 'Loading ' + label + '...' });
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
async function renderStatusBar() {
|
|
324
|
-
try {
|
|
325
|
-
const status = await getJson('/status');
|
|
326
|
-
const pill = document.getElementById('loop-pill');
|
|
327
|
-
pill.textContent = status.loopState;
|
|
328
|
-
pill.className = 'pill pill-' + status.loopState;
|
|
329
|
-
const pauseButton = document.getElementById('pause-toggle');
|
|
330
|
-
pauseButton.dataset.paused = String(status.paused);
|
|
331
|
-
pauseButton.textContent = status.paused ? 'Resume' : 'Pause';
|
|
332
|
-
pauseButton.classList.toggle('is-paused', status.paused);
|
|
333
|
-
const freshness = status.sourceFreshness.level;
|
|
334
|
-
const summary = document.getElementById('status-summary');
|
|
335
|
-
summary.textContent =
|
|
336
|
-
'runs today: ' + status.runsToday + ' · failures today: ' + status.failuresToday +
|
|
337
|
-
' · cost today: $' + Number(status.costUsdToday ?? 0).toFixed(2) +
|
|
338
|
-
' · source freshness: ' + freshness +
|
|
339
|
-
(status.lastRun ? ' · last run: ' + status.lastRun.repo + '#' + status.lastRun.issueNumber + ' ' + status.lastRun.action + ' → ' + (status.lastRun.sentinel ?? status.lastRun.status) : '');
|
|
340
|
-
} catch (err) {
|
|
341
|
-
document.getElementById('status-summary').textContent = 'status unavailable: ' + err.message;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
async function togglePause() {
|
|
346
|
-
const button = document.getElementById('pause-toggle');
|
|
347
|
-
const paused = button.dataset.paused === 'true';
|
|
348
|
-
button.disabled = true;
|
|
349
|
-
button.textContent = paused ? 'Resuming...' : 'Pausing...';
|
|
350
|
-
try {
|
|
351
|
-
if (paused) await deleteJson('/pause');
|
|
352
|
-
else await postJson('/pause');
|
|
353
|
-
await renderStatusBar();
|
|
354
|
-
switchView(currentView, { showLoading: false });
|
|
355
|
-
} catch (err) {
|
|
356
|
-
document.getElementById('status-summary').textContent = 'pause toggle failed: ' + err.message;
|
|
357
|
-
} finally {
|
|
358
|
-
button.disabled = false;
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
362
|
-
async function forceTickNow() {
|
|
363
|
-
const button = document.getElementById('force-tick');
|
|
364
|
-
button.disabled = true;
|
|
365
|
-
const original = button.textContent;
|
|
366
|
-
button.textContent = 'Requested';
|
|
367
|
-
try {
|
|
368
|
-
await postJson('/tick');
|
|
369
|
-
await renderStatusBar();
|
|
370
|
-
} catch (err) {
|
|
371
|
-
document.getElementById('status-summary').textContent = 'tick request failed: ' + err.message;
|
|
372
|
-
} finally {
|
|
373
|
-
setTimeout(() => {
|
|
374
|
-
button.disabled = false;
|
|
375
|
-
button.textContent = original;
|
|
376
|
-
}, 900);
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
async function renderBoard(context) {
|
|
381
|
-
const board = await getJson('/board', context.signal);
|
|
382
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
383
|
-
const main = document.getElementById('main');
|
|
384
|
-
const collapsedColumns = readCollapsedColumns();
|
|
385
|
-
const columns = el('div', { class: 'columns' }, CONDITIONS.map((cond) => {
|
|
386
|
-
const items = board.filter((c) => c.condition === cond);
|
|
387
|
-
if (cond === 'finished') items.sort((a, b) => a.timeInStageMs - b.timeInStageMs);
|
|
388
|
-
const cards = items.map((item) => {
|
|
389
|
-
const cardCondClass = COND_CARD[item.condition] ?? '';
|
|
390
|
-
return el('div', {
|
|
391
|
-
class: 'card' + (cardCondClass ? ' ' + cardCondClass : ''),
|
|
392
|
-
onclick: () => openItemModal(item.repo, item.number, item),
|
|
393
|
-
}, [
|
|
394
|
-
el('div', { class: 'title', text: item.repo + '#' + item.number + ' ' + item.title }),
|
|
395
|
-
...renderCardSummaryNodes(item),
|
|
396
|
-
]);
|
|
397
|
-
});
|
|
398
|
-
const isCollapsed = collapsedColumns.has(cond);
|
|
399
|
-
const toggle = el('button', {
|
|
400
|
-
class: 'col-toggle',
|
|
401
|
-
type: 'button',
|
|
402
|
-
onclick: () => {
|
|
403
|
-
const nextCollapsed = !collapsedColumns.has(cond);
|
|
404
|
-
if (nextCollapsed) collapsedColumns.add(cond);
|
|
405
|
-
else collapsedColumns.delete(cond);
|
|
406
|
-
writeCollapsedColumns(collapsedColumns);
|
|
407
|
-
setColumnCollapsed(column, toggle, nextCollapsed);
|
|
408
|
-
},
|
|
409
|
-
});
|
|
410
|
-
const column = el('div', { class: 'col' + (items.length === 0 ? ' col-empty' : '') }, [
|
|
411
|
-
el('div', { class: 'col-header' }, [
|
|
412
|
-
el('h2', { text: cond + ' (' + items.length + ')' }),
|
|
413
|
-
toggle,
|
|
414
|
-
]),
|
|
415
|
-
el('div', { class: 'col-cards' }, cards),
|
|
416
|
-
]);
|
|
417
|
-
setColumnCollapsed(column, toggle, isCollapsed);
|
|
418
|
-
return column;
|
|
419
|
-
}));
|
|
420
|
-
main.replaceChildren(columns);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
function renderChildRun(run) {
|
|
424
|
-
return el('div', { class: 'child-run' }, [
|
|
425
|
-
el('span', { class: 'dot' + (run.isWatcher ? ' dot-watch' : '') }),
|
|
426
|
-
el('div', { class: 'run-title', text: (run.isWatcher ? 'watcher ' : '') + run.action + ' running' }),
|
|
427
|
-
el('div', {
|
|
428
|
-
class: 'run-meta',
|
|
429
|
-
text: [run.runnerName, run.tier, fmtMs(run.ageMs)].filter(Boolean).join(' | '),
|
|
430
|
-
}),
|
|
431
|
-
]);
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function renderCardSummaryNodes(item) {
|
|
435
|
-
const nonWakeLabels = (item.labels || []).filter((l) => !l.startsWith('wake:'));
|
|
436
|
-
const pillClass = 'pill ' + (COND_PILL[item.condition] || 'pill-finished');
|
|
437
|
-
const statsText = item.totalRuns + ' runs | ' + fmtCost(item.totalCostUsd) + ' | ' + fmtCompact(item.totalTokens) + ' tokens';
|
|
438
|
-
const isFinished = item.condition === 'finished';
|
|
439
|
-
const displayStatus = item.displayStatus || item.lastRunStatus || item.stage;
|
|
440
|
-
const metaPills = [
|
|
441
|
-
el('span', { class: pillClass, text: displayStatus.replace(/-/g, ' ') }),
|
|
442
|
-
...(item.isFrozen ? [el('span', { class: 'pill pill-frozen', text: 'Frozen' })] : []),
|
|
443
|
-
el('span', { class: 'chip chip-meta', text: item.workflow }),
|
|
444
|
-
el('span', { class: 'chip chip-meta', text: item.stage }),
|
|
445
|
-
];
|
|
446
|
-
if (!isFinished) {
|
|
447
|
-
metaPills.push(el('span', { class: 'chip chip-meta', text: fmtMs(item.timeInStageMs) + ' in stage' }));
|
|
448
|
-
}
|
|
449
|
-
return [
|
|
450
|
-
el('div', { class: 'meta', style: 'display:flex;align-items:center;gap:0.3rem;flex-wrap:wrap;margin-top:0.2rem;' }, metaPills),
|
|
451
|
-
...(nonWakeLabels.length > 0
|
|
452
|
-
? [el('div', { class: 'meta', style: 'margin-top:0.2rem;' }, nonWakeLabels.map((label) => el('span', { class: 'chip chip-label', text: label })))]
|
|
453
|
-
: []),
|
|
454
|
-
el('div', { class: 'card-stats', text: statsText }),
|
|
455
|
-
...(item.activeMainRun ? [renderChildRun(item.activeMainRun)] : []),
|
|
456
|
-
...((item.activeChildRuns || []).map(renderChildRun)),
|
|
457
|
-
];
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
function resourceUriToUrl(resourceUri) {
|
|
461
|
-
const parts = resourceUri.split(':');
|
|
462
|
-
if (parts.length < 3) return null;
|
|
463
|
-
const provider = parts[0];
|
|
464
|
-
const type = parts[1];
|
|
465
|
-
const locator = parts.slice(2).join(':');
|
|
466
|
-
const m = locator.match(/^(.+)#([0-9]+)$/);
|
|
467
|
-
if (!m) return null;
|
|
468
|
-
if (provider === 'github') {
|
|
469
|
-
if (type === 'issue') return 'https://github.com/' + m[1] + '/issues/' + m[2];
|
|
470
|
-
if (type === 'pr') return 'https://github.com/' + m[1] + '/pull/' + m[2];
|
|
471
|
-
}
|
|
472
|
-
return null;
|
|
473
|
-
}
|
|
474
|
-
|
|
475
|
-
async function openItemModal(repo, number, boardItem) {
|
|
476
|
-
const overlay = document.getElementById('modal-overlay');
|
|
477
|
-
const title = document.getElementById('modal-title');
|
|
478
|
-
const tabs = document.getElementById('modal-tabs');
|
|
479
|
-
const body = document.getElementById('modal-body');
|
|
480
|
-
const loaded = new Map();
|
|
481
|
-
body.textContent = 'Loading...';
|
|
482
|
-
tabs.innerHTML = '';
|
|
483
|
-
title.textContent = repo + '#' + number;
|
|
484
|
-
overlay.classList.add('open');
|
|
485
|
-
|
|
486
|
-
for (const tab of ['details', 'events', 'transcripts']) {
|
|
487
|
-
tabs.appendChild(el('button', {
|
|
488
|
-
type: 'button',
|
|
489
|
-
class: 'nav-button',
|
|
490
|
-
text: tab[0].toUpperCase() + tab.slice(1),
|
|
491
|
-
onclick: () => switchItemTab(tab),
|
|
492
|
-
}));
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
async function ensureDetail() {
|
|
496
|
-
if (!loaded.has('detail')) {
|
|
497
|
-
const itemPath =
|
|
498
|
-
boardItem && boardItem.workItemKey
|
|
499
|
-
? '/items/' + encodeURIComponent(boardItem.workItemKey)
|
|
500
|
-
: '/items/' + encodeURIComponent(repo) + '/' + number;
|
|
501
|
-
loaded.set('detail', await getJson(itemPath));
|
|
502
|
-
}
|
|
503
|
-
return loaded.get('detail');
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
async function switchItemTab(tab) {
|
|
507
|
-
for (const button of tabs.querySelectorAll('button')) {
|
|
508
|
-
button.classList.toggle('active', button.textContent.toLowerCase() === tab);
|
|
509
|
-
}
|
|
510
|
-
body.scrollTop = 0;
|
|
511
|
-
if (!loaded.has(tab)) {
|
|
512
|
-
body.textContent = 'Loading...';
|
|
513
|
-
loaded.set(tab, await renderItemTab(tab));
|
|
514
|
-
}
|
|
515
|
-
body.replaceChildren(loaded.get(tab));
|
|
516
|
-
if (tab === 'transcripts') {
|
|
517
|
-
body.scrollTop = body.scrollHeight;
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
async function renderItemTab(tab) {
|
|
522
|
-
const detail = await ensureDetail();
|
|
523
|
-
if (!detail) return el('p', { text: 'Not found' });
|
|
524
|
-
if (tab === 'events') {
|
|
525
|
-
return renderItemEvents(detail.events || []);
|
|
526
|
-
}
|
|
527
|
-
if (tab === 'transcripts') {
|
|
528
|
-
const transcripts = await getJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/transcripts');
|
|
529
|
-
return renderTranscripts(transcripts);
|
|
530
|
-
}
|
|
531
|
-
return renderItemDetails(detail, boardItem);
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
await switchItemTab('details');
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
function eventTimestamp(event) {
|
|
538
|
-
return event.ingestedAt || event.occurredAt || '';
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
function eventDirectionSymbol(direction) {
|
|
542
|
-
if (direction === 'inbound') return '\\u2193';
|
|
543
|
-
if (direction === 'outbound') return '\\u2191';
|
|
544
|
-
return '\\u2194';
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
function sortedEventsNewestFirst(events) {
|
|
548
|
-
return [...events].sort((left, right) => eventTimestamp(right).localeCompare(eventTimestamp(left)));
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
async function copyText(text, button) {
|
|
552
|
-
const original = button.textContent;
|
|
553
|
-
try {
|
|
554
|
-
if (navigator.clipboard && window.isSecureContext) {
|
|
555
|
-
await navigator.clipboard.writeText(text);
|
|
556
|
-
} else {
|
|
557
|
-
const input = document.createElement('textarea');
|
|
558
|
-
input.value = text;
|
|
559
|
-
input.setAttribute('readonly', 'readonly');
|
|
560
|
-
input.style.position = 'fixed';
|
|
561
|
-
input.style.top = '-1000px';
|
|
562
|
-
document.body.appendChild(input);
|
|
563
|
-
input.select();
|
|
564
|
-
document.execCommand('copy');
|
|
565
|
-
input.remove();
|
|
566
|
-
}
|
|
567
|
-
button.textContent = 'Copied';
|
|
568
|
-
} catch (err) {
|
|
569
|
-
button.textContent = 'Copy failed';
|
|
570
|
-
} finally {
|
|
571
|
-
setTimeout(() => {
|
|
572
|
-
button.textContent = original;
|
|
573
|
-
}, 1100);
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
function renderItemEvents(events) {
|
|
578
|
-
if (events.length === 0) {
|
|
579
|
-
return el('p', { class: 'meta', text: 'No events available for this item.' });
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
const root = el('div', { class: 'event-list' });
|
|
583
|
-
for (const event of sortedEventsNewestFirst(events)) {
|
|
584
|
-
const json = JSON.stringify(event, null, 2);
|
|
585
|
-
const card = el('article', { class: 'event-card' });
|
|
586
|
-
const summary = el('button', { type: 'button', class: 'event-summary' }, [
|
|
587
|
-
el('span', { class: 'event-time', text: eventTimestamp(event) }),
|
|
588
|
-
el('span', { class: 'event-type', text: event.sourceEventType || 'unknown event' }),
|
|
589
|
-
el('span', { class: 'event-id', text: event.eventId || '' }),
|
|
590
|
-
el('span', { class: 'event-direction', title: event.direction || 'unknown', text: eventDirectionSymbol(event.direction) }),
|
|
591
|
-
]);
|
|
592
|
-
summary.addEventListener('click', () => {
|
|
593
|
-
card.classList.toggle('expanded');
|
|
594
|
-
summary.setAttribute('aria-expanded', String(card.classList.contains('expanded')));
|
|
595
|
-
});
|
|
596
|
-
const copyButton = el('button', { type: 'button', class: 'event-copy', text: 'Copy' });
|
|
597
|
-
copyButton.addEventListener('click', (clickEvent) => {
|
|
598
|
-
clickEvent.stopPropagation();
|
|
599
|
-
copyText(json, copyButton);
|
|
600
|
-
});
|
|
601
|
-
const jsonArea = el('div', { class: 'event-json' }, [
|
|
602
|
-
copyButton,
|
|
603
|
-
el('pre', { text: json }),
|
|
604
|
-
]);
|
|
605
|
-
card.appendChild(summary);
|
|
606
|
-
card.appendChild(jsonArea);
|
|
607
|
-
root.appendChild(card);
|
|
608
|
-
}
|
|
609
|
-
return root;
|
|
610
|
-
}
|
|
611
|
-
|
|
612
|
-
function renderItemDetails(detail, boardItem) {
|
|
613
|
-
const body = el('div');
|
|
614
|
-
const repo = detail.item.issue.repo;
|
|
615
|
-
const number = detail.item.issue.number;
|
|
616
|
-
const headLink = el('a', { href: detail.item.issue.url, target: '_blank', rel: 'noopener noreferrer', text: repo + '#' + number });
|
|
617
|
-
body.appendChild(el('h3', {}, [headLink]));
|
|
618
|
-
body.appendChild(el('p', { text: detail.item.issue.title }));
|
|
619
|
-
if (boardItem) {
|
|
620
|
-
body.appendChild(el('div', { class: 'item-summary' }, renderCardSummaryNodes(boardItem)));
|
|
621
|
-
}
|
|
622
|
-
const details = [];
|
|
623
|
-
if (detail.item.wake.sessionId) {
|
|
624
|
-
details.push(['Session', detail.item.wake.sessionId]);
|
|
625
|
-
}
|
|
626
|
-
if (detail.item.wake.workspacePath) {
|
|
627
|
-
details.push(['Workspace', detail.item.wake.workspacePath]);
|
|
628
|
-
}
|
|
629
|
-
if (details.length > 0) {
|
|
630
|
-
body.appendChild(el('dl', { class: 'detail-kv' }, details.flatMap(([label, value]) => [
|
|
631
|
-
el('dt', { text: label }),
|
|
632
|
-
el('dd', { text: value }),
|
|
633
|
-
])));
|
|
634
|
-
}
|
|
635
|
-
const isFrozen = detail.item.context.frozen !== undefined && detail.item.context.frozen !== null;
|
|
636
|
-
const actionBar = el('div', { class: 'action-bar' });
|
|
637
|
-
const lastRun = detail.runs.at(-1);
|
|
638
|
-
if (lastRun && lastRun.sentinel === 'FAILED') {
|
|
639
|
-
const retryBtn = el('button', { type: 'button', class: 'btn', text: 'Retry' });
|
|
640
|
-
retryBtn.addEventListener('click', async () => {
|
|
641
|
-
retryBtn.disabled = true;
|
|
642
|
-
retryBtn.textContent = 'Queuing retry...';
|
|
643
|
-
try {
|
|
644
|
-
await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/retry');
|
|
645
|
-
retryBtn.textContent = 'Retry queued';
|
|
646
|
-
} catch (err) {
|
|
647
|
-
retryBtn.disabled = false;
|
|
648
|
-
retryBtn.textContent = 'Retry';
|
|
649
|
-
document.getElementById('status-summary').textContent = 'retry failed: ' + err.message;
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
actionBar.appendChild(retryBtn);
|
|
653
|
-
}
|
|
654
|
-
if (detail.item.issue.labels.includes('wake:scheduled-workflow')) {
|
|
655
|
-
const runNowBtn = el('button', { type: 'button', class: 'btn', text: 'Run now' });
|
|
656
|
-
runNowBtn.addEventListener('click', async () => {
|
|
657
|
-
runNowBtn.disabled = true;
|
|
658
|
-
runNowBtn.textContent = 'Requesting run...';
|
|
659
|
-
try {
|
|
660
|
-
await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/run');
|
|
661
|
-
runNowBtn.textContent = 'Run requested';
|
|
662
|
-
} catch (err) {
|
|
663
|
-
runNowBtn.disabled = false;
|
|
664
|
-
runNowBtn.textContent = 'Run now';
|
|
665
|
-
document.getElementById('status-summary').textContent = 'run request failed: ' + err.message;
|
|
666
|
-
}
|
|
667
|
-
});
|
|
668
|
-
actionBar.appendChild(runNowBtn);
|
|
669
|
-
}
|
|
670
|
-
const freezeBtn = el('button', { type: 'button', class: 'btn', text: isFrozen ? 'Unfreeze' : 'Freeze' });
|
|
671
|
-
freezeBtn.addEventListener('click', async () => {
|
|
672
|
-
const action = isFrozen ? 'unfreeze' : 'freeze';
|
|
673
|
-
freezeBtn.disabled = true;
|
|
674
|
-
freezeBtn.textContent = isFrozen ? 'Unfreezing...' : 'Freezing...';
|
|
675
|
-
try {
|
|
676
|
-
await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/' + action);
|
|
677
|
-
freezeBtn.textContent = isFrozen ? 'Unfrozen' : 'Frozen';
|
|
678
|
-
document.getElementById('status-summary').textContent = isFrozen ? 'work item unfrozen' : 'work item frozen';
|
|
679
|
-
} catch (err) {
|
|
680
|
-
freezeBtn.disabled = false;
|
|
681
|
-
freezeBtn.textContent = isFrozen ? 'Unfreeze' : 'Freeze';
|
|
682
|
-
document.getElementById('status-summary').textContent = action + ' failed: ' + err.message;
|
|
683
|
-
}
|
|
684
|
-
});
|
|
685
|
-
actionBar.appendChild(freezeBtn);
|
|
686
|
-
|
|
687
|
-
const deleteBtn = el('button', { type: 'button', class: 'btn danger', text: 'Delete' });
|
|
688
|
-
deleteBtn.addEventListener('click', async () => {
|
|
689
|
-
if (!confirm('Delete this work item from the board and remove its resource correlations?')) return;
|
|
690
|
-
deleteBtn.disabled = true;
|
|
691
|
-
deleteBtn.textContent = 'Deleting...';
|
|
692
|
-
try {
|
|
693
|
-
await postJson('/work-items/' + encodeURIComponent(detail.item.workItemKey) + '/delete');
|
|
694
|
-
deleteBtn.textContent = 'Deleted';
|
|
695
|
-
document.getElementById('status-summary').textContent = 'work item deleted';
|
|
696
|
-
switchView(currentView, { showLoading: false });
|
|
697
|
-
} catch (err) {
|
|
698
|
-
deleteBtn.disabled = false;
|
|
699
|
-
deleteBtn.textContent = 'Delete';
|
|
700
|
-
document.getElementById('status-summary').textContent = 'delete failed: ' + err.message;
|
|
701
|
-
}
|
|
702
|
-
});
|
|
703
|
-
actionBar.appendChild(deleteBtn);
|
|
704
|
-
if (actionBar.childNodes.length > 0) {
|
|
705
|
-
body.appendChild(actionBar);
|
|
706
|
-
}
|
|
707
|
-
const resources = detail.item.correlatedResources || [];
|
|
708
|
-
if (resources.length > 0) {
|
|
709
|
-
body.appendChild(el('h3', { text: 'Resources' }));
|
|
710
|
-
const resourceList = el('ul', { class: 'resource-list' });
|
|
711
|
-
for (const res of resources) {
|
|
712
|
-
const resUrl = resourceUriToUrl(res.resourceUri);
|
|
713
|
-
const badge = el('span', { class: 'chip', text: res.role });
|
|
714
|
-
const linkOrText = resUrl
|
|
715
|
-
? el('a', { href: resUrl, target: '_blank', rel: 'noopener noreferrer', text: res.resourceUri })
|
|
716
|
-
: el('span', { class: 'meta', text: res.resourceUri });
|
|
717
|
-
resourceList.appendChild(el('li', {}, [badge, linkOrText]));
|
|
718
|
-
}
|
|
719
|
-
body.appendChild(resourceList);
|
|
720
|
-
}
|
|
721
|
-
body.appendChild(el('h3', { text: 'Runs' }));
|
|
722
|
-
body.appendChild(el('table', {}, [
|
|
723
|
-
el('tr', {}, ['action', 'status', 'sentinel', 'started', 'runId'].map((h) => el('th', { text: h }))),
|
|
724
|
-
...detail.runs.map((r) => el('tr', {}, [
|
|
725
|
-
el('td', { text: r.action }), el('td', { text: r.status }), el('td', { text: r.sentinel || '' }),
|
|
726
|
-
el('td', { text: r.startedAt }), el('td', { text: r.runId }),
|
|
727
|
-
])),
|
|
728
|
-
]));
|
|
729
|
-
body.appendChild(el('h3', { text: 'Context' }));
|
|
730
|
-
body.appendChild(el('pre', { text: JSON.stringify(detail.item.context, null, 2) }));
|
|
731
|
-
return body;
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
function renderTranscripts(transcripts) {
|
|
735
|
-
if (!transcripts.enabled) {
|
|
736
|
-
return el('p', { class: 'meta', text: 'Transcripts are not enabled \\u2014 set transcripts.enabled: true in config to turn them on.' });
|
|
737
|
-
}
|
|
738
|
-
if (!transcripts.sessions || transcripts.sessions.length === 0) {
|
|
739
|
-
return el('p', { class: 'meta', text: 'No transcripts available for this item.' });
|
|
740
|
-
}
|
|
741
|
-
const root = el('div');
|
|
742
|
-
for (const session of transcripts.sessions) {
|
|
743
|
-
const firstStartedAt = session.entries[0] ? session.entries[0].startedAt : '';
|
|
744
|
-
const sessionLabel = session.sessionId || session.sessionKey;
|
|
745
|
-
root.appendChild(
|
|
746
|
-
el('div', {
|
|
747
|
-
class: 'transcript-session',
|
|
748
|
-
text:
|
|
749
|
-
'\\u2014 new session \\u00b7 ' +
|
|
750
|
-
sessionLabel +
|
|
751
|
-
' \\u00b7 ' +
|
|
752
|
-
firstStartedAt +
|
|
753
|
-
' \\u2014',
|
|
754
|
-
}),
|
|
755
|
-
);
|
|
756
|
-
for (const entry of session.entries) {
|
|
757
|
-
root.appendChild(el('article', { class: 'transcript-entry' }, [
|
|
758
|
-
el('div', { class: 'transcript-head', text: entry.role + ' \\u00b7 ' + entry.action + ' \\u00b7 ' + entry.cli + ' \\u00b7 ' + entry.startedAt }),
|
|
759
|
-
el('pre', { class: 'transcript-text', text: entry.text }),
|
|
760
|
-
]));
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
return root;
|
|
764
|
-
}
|
|
765
|
-
|
|
766
|
-
async function renderActivity(context) {
|
|
767
|
-
const events = await getJson('/events?limit=200', context.signal);
|
|
768
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
769
|
-
const main = document.getElementById('main');
|
|
770
|
-
main.replaceChildren(el('table', {}, [
|
|
771
|
-
el('tr', {}, ['time', 'direction', 'type', 'work item'].map((h) => el('th', { text: h }))),
|
|
772
|
-
...events.map((ev) => el('tr', {}, [
|
|
773
|
-
el('td', { text: ev.ingestedAt }), el('td', { text: ev.direction }),
|
|
774
|
-
el('td', { text: ev.sourceEventType }), el('td', { text: ev.workItemKey }),
|
|
775
|
-
])),
|
|
776
|
-
]));
|
|
777
|
-
}
|
|
778
|
-
|
|
779
|
-
function fmtCost(usd) {
|
|
780
|
-
if (usd === undefined || usd === null) return '';
|
|
781
|
-
return '$' + Number(usd).toFixed(usd < 1 ? 4 : 2);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
function fmtTokens(tokenUsage) {
|
|
785
|
-
if (!tokenUsage) return '';
|
|
786
|
-
const total =
|
|
787
|
-
(tokenUsage.inputTokens || 0) + (tokenUsage.outputTokens || 0) +
|
|
788
|
-
(tokenUsage.cacheCreationInputTokens || 0) + (tokenUsage.cacheReadInputTokens || 0);
|
|
789
|
-
return total >= 1000 ? (total / 1000).toFixed(1) + 'k' : String(total);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
function fmtCompact(value) {
|
|
793
|
-
const n = Number(value || 0);
|
|
794
|
-
if (n >= 1000000) return (n / 1000000).toFixed(1) + 'M';
|
|
795
|
-
if (n >= 1000) return (n / 1000).toFixed(1) + 'k';
|
|
796
|
-
return String(n);
|
|
797
|
-
}
|
|
798
|
-
|
|
799
|
-
function fmtNumber(value) {
|
|
800
|
-
return Number(value || 0).toLocaleString();
|
|
801
|
-
}
|
|
802
|
-
|
|
803
|
-
function fmtDuration(ms) {
|
|
804
|
-
return ms === undefined || ms === null ? '' : fmtMs(ms);
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
function metricBar(value, max) {
|
|
808
|
-
const pct = max > 0 ? Math.max(2, Math.round((value / max) * 100)) : 0;
|
|
809
|
-
return el('div', { class: 'metric-bar' }, [
|
|
810
|
-
el('div', { class: 'metric-bar-fill', style: 'width:' + pct + '%' }),
|
|
811
|
-
]);
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
function stackedBar(segments) {
|
|
815
|
-
const total = segments.reduce((sum, segment) => sum + segment.value, 0);
|
|
816
|
-
return el('div', { class: 'stacked-bar' }, segments.filter((segment) => segment.value > 0).map((segment) =>
|
|
817
|
-
el('div', {
|
|
818
|
-
class: 'stacked-segment',
|
|
819
|
-
title: segment.label + ': ' + fmtNumber(segment.value),
|
|
820
|
-
style: 'width:' + ((segment.value / total) * 100).toFixed(2) + '%;background:' + segment.color,
|
|
821
|
-
}),
|
|
822
|
-
));
|
|
823
|
-
}
|
|
824
|
-
|
|
825
|
-
function selectBox(value, options, onChange) {
|
|
826
|
-
return el('select', { onchange: (event) => onChange(event.target.value) }, options.map((option) =>
|
|
827
|
-
el('option', {
|
|
828
|
-
value: option[0],
|
|
829
|
-
text: option[1],
|
|
830
|
-
...(option[0] === value ? { selected: 'selected' } : {}),
|
|
831
|
-
}),
|
|
832
|
-
));
|
|
833
|
-
}
|
|
834
|
-
|
|
835
|
-
function metricValueCell(value, max, format) {
|
|
836
|
-
return el('td', {}, [
|
|
837
|
-
el('div', { class: 'metric-table-value' }, [
|
|
838
|
-
el('span', { text: format(value) }),
|
|
839
|
-
metricBar(value, max),
|
|
840
|
-
]),
|
|
841
|
-
]);
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
function renderCountTable(rows, label) {
|
|
845
|
-
if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
|
|
846
|
-
const max = Math.max(...rows.map((row) => row.count));
|
|
847
|
-
return el('table', {}, [
|
|
848
|
-
el('tr', {}, [el('th', { text: label }), el('th', { text: 'runs' })]),
|
|
849
|
-
...rows.map((row) => el('tr', {}, [
|
|
850
|
-
el('td', { text: row.key }),
|
|
851
|
-
metricValueCell(row.count, max, fmtNumber),
|
|
852
|
-
])),
|
|
853
|
-
]);
|
|
854
|
-
}
|
|
855
|
-
|
|
856
|
-
function renderTokenTable(rows, label) {
|
|
857
|
-
if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
|
|
858
|
-
const max = Math.max(...rows.map((row) => row.tokens));
|
|
859
|
-
return el('table', {}, [
|
|
860
|
-
el('tr', {}, [
|
|
861
|
-
el('th', { text: label }),
|
|
862
|
-
el('th', { text: 'tokens' }),
|
|
863
|
-
el('th', { text: 'avg/run' }),
|
|
864
|
-
el('th', { text: 'cost' }),
|
|
865
|
-
el('th', { text: 'avg cost/run' }),
|
|
866
|
-
el('th', { text: 'runs' }),
|
|
867
|
-
]),
|
|
868
|
-
...rows.map((row) => el('tr', {}, [
|
|
869
|
-
el('td', { text: row.key }),
|
|
870
|
-
metricValueCell(row.tokens, max, fmtNumber),
|
|
871
|
-
el('td', { text: fmtNumber(row.averageTokens || 0) }),
|
|
872
|
-
el('td', { text: fmtCost(row.costUsd) }),
|
|
873
|
-
el('td', { text: fmtCost(row.averageCostUsd) }),
|
|
874
|
-
el('td', { text: fmtNumber(row.count || 0) }),
|
|
875
|
-
])),
|
|
876
|
-
]);
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
function renderDurationTable(rows, label) {
|
|
880
|
-
if (rows.length === 0) return el('p', { class: 'meta', text: 'No data for this window.' });
|
|
881
|
-
const max = Math.max(...rows.map((row) => row.totalMs || row.medianMs));
|
|
882
|
-
return el('table', {}, [
|
|
883
|
-
el('tr', {}, [
|
|
884
|
-
el('th', { text: label }),
|
|
885
|
-
el('th', { text: 'total' }),
|
|
886
|
-
el('th', { text: 'median' }),
|
|
887
|
-
el('th', { text: 'average' }),
|
|
888
|
-
el('th', { text: 'runs' }),
|
|
889
|
-
]),
|
|
890
|
-
...rows.map((row) => el('tr', {}, [
|
|
891
|
-
el('td', { text: row.key || row.label }),
|
|
892
|
-
metricValueCell(row.totalMs || 0, max, fmtDuration),
|
|
893
|
-
el('td', { text: fmtDuration(row.medianMs) }),
|
|
894
|
-
el('td', { text: fmtDuration(row.averageMs) }),
|
|
895
|
-
el('td', { text: fmtNumber(row.count) }),
|
|
896
|
-
])),
|
|
897
|
-
]);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
function renderRunsOverTime(rows) {
|
|
901
|
-
return el('table', {}, [
|
|
902
|
-
el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'runs' }), el('th', { text: 'status split' })]),
|
|
903
|
-
...rows.map((row) => el('tr', {}, [
|
|
904
|
-
el('td', { text: row.label }),
|
|
905
|
-
el('td', { text: fmtNumber(row.total) }),
|
|
906
|
-
el('td', {}, [stackedBar([
|
|
907
|
-
{ label: 'completed', value: row.completed, color: '#7fe3a3' },
|
|
908
|
-
{ label: 'blocked', value: row.blocked, color: '#ffcf7f' },
|
|
909
|
-
{ label: 'awaiting approval', value: row.awaitingApproval, color: '#7fb3ff' },
|
|
910
|
-
{ label: 'failed', value: row.failed, color: '#ff8f7f' },
|
|
911
|
-
{ label: 'other', value: row.other, color: '#9aa2ad' },
|
|
912
|
-
])]),
|
|
913
|
-
])),
|
|
914
|
-
]);
|
|
915
|
-
}
|
|
916
|
-
|
|
917
|
-
function renderRunsByStatusOverTime(rows) {
|
|
918
|
-
return el('table', {}, [
|
|
919
|
-
el('tr', {}, [
|
|
920
|
-
el('th', { text: 'bucket' }),
|
|
921
|
-
el('th', { text: 'completed' }),
|
|
922
|
-
el('th', { text: 'blocked' }),
|
|
923
|
-
el('th', { text: 'awaiting approval' }),
|
|
924
|
-
el('th', { text: 'failed' }),
|
|
925
|
-
el('th', { text: 'other' }),
|
|
926
|
-
el('th', { text: 'total' }),
|
|
927
|
-
]),
|
|
928
|
-
...rows.map((row) => el('tr', {}, [
|
|
929
|
-
el('td', { text: row.label }),
|
|
930
|
-
el('td', { text: fmtNumber(row.completed) }),
|
|
931
|
-
el('td', { text: fmtNumber(row.blocked) }),
|
|
932
|
-
el('td', { text: fmtNumber(row.awaitingApproval) }),
|
|
933
|
-
el('td', { text: fmtNumber(row.failed) }),
|
|
934
|
-
el('td', { text: fmtNumber(row.other) }),
|
|
935
|
-
el('td', { text: fmtNumber(row.total) }),
|
|
936
|
-
])),
|
|
937
|
-
]);
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
function renderTokensOverTime(rows) {
|
|
941
|
-
return el('table', {}, [
|
|
942
|
-
el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'tokens' }), el('th', { text: 'token split' }), el('th', { text: 'cost' })]),
|
|
943
|
-
...rows.map((row) => el('tr', {}, [
|
|
944
|
-
el('td', { text: row.label }),
|
|
945
|
-
el('td', { text: fmtNumber(row.tokens) }),
|
|
946
|
-
el('td', {}, [stackedBar([
|
|
947
|
-
{ label: 'input', value: row.inputTokens, color: '#7fb3ff' },
|
|
948
|
-
{ label: 'output', value: row.outputTokens, color: '#7fe3a3' },
|
|
949
|
-
{ label: 'cache creation', value: row.cacheCreationInputTokens, color: '#c79bff' },
|
|
950
|
-
{ label: 'cache read', value: row.cacheReadInputTokens, color: '#ffcf7f' },
|
|
951
|
-
])]),
|
|
952
|
-
el('td', { text: fmtCost(row.costUsd) }),
|
|
953
|
-
])),
|
|
954
|
-
]);
|
|
955
|
-
}
|
|
956
|
-
|
|
957
|
-
function renderWorkItemsOverTime(rows) {
|
|
958
|
-
const max = Math.max(0, ...rows.map((row) => row.completed));
|
|
959
|
-
return el('table', {}, [
|
|
960
|
-
el('tr', {}, [el('th', { text: 'bucket' }), el('th', { text: 'completed' })]),
|
|
961
|
-
...rows.map((row) => el('tr', {}, [
|
|
962
|
-
el('td', { text: row.label }),
|
|
963
|
-
metricValueCell(row.completed, max, fmtNumber),
|
|
964
|
-
])),
|
|
965
|
-
]);
|
|
966
|
-
}
|
|
967
|
-
|
|
968
|
-
function renderWorkItemDurations(rows) {
|
|
969
|
-
if (rows.length === 0) return el('p', { class: 'meta', text: 'No completed work items for this window.' });
|
|
970
|
-
const max = Math.max(...rows.map((row) => row.durationMs));
|
|
971
|
-
return el('table', {}, [
|
|
972
|
-
el('tr', {}, ['work item', 'duration', 'completed'].map((h) => el('th', { text: h }))),
|
|
973
|
-
...rows.map((row) => el('tr', {}, [
|
|
974
|
-
el('td', { text: row.repo + '#' + row.issueNumber }),
|
|
975
|
-
metricValueCell(row.durationMs, max, fmtDuration),
|
|
976
|
-
el('td', { text: row.completedAt }),
|
|
977
|
-
])),
|
|
978
|
-
]);
|
|
979
|
-
}
|
|
980
|
-
|
|
981
|
-
function renderWorkItemTotals(rows) {
|
|
982
|
-
if (rows.length === 0) return el('p', { class: 'meta', text: 'No runs for this window.' });
|
|
983
|
-
const maxTokens = Math.max(...rows.map((row) => row.totalTokens));
|
|
984
|
-
return el('table', {}, [
|
|
985
|
-
el('tr', {}, ['work item', 'runs', 'total tokens', 'total duration'].map((h) => el('th', { text: h }))),
|
|
986
|
-
...rows.map((row) => el('tr', {}, [
|
|
987
|
-
el('td', { text: row.key }),
|
|
988
|
-
el('td', { text: String(row.runCount) }),
|
|
989
|
-
metricValueCell(row.totalTokens, maxTokens, fmtNumber),
|
|
990
|
-
el('td', { text: fmtMs(row.totalDurationMs) }),
|
|
991
|
-
])),
|
|
992
|
-
]);
|
|
993
|
-
}
|
|
994
|
-
|
|
995
|
-
function renderMetricDetail(detail) {
|
|
996
|
-
if (detail.kind === 'runs-over-time') return renderRunsOverTime(detail.rows);
|
|
997
|
-
if (detail.kind === 'runs-by-status-over-time') return renderRunsByStatusOverTime(detail.rows);
|
|
998
|
-
if (detail.kind === 'run-counts') return renderCountTable(detail.rows, detail.group);
|
|
999
|
-
if (detail.kind === 'tokens-over-time') return renderTokensOverTime(detail.rows);
|
|
1000
|
-
if (detail.kind === 'token-counts') return renderTokenTable(detail.rows, detail.group);
|
|
1001
|
-
if (detail.kind === 'durations') return renderDurationTable(detail.rows, detail.group);
|
|
1002
|
-
if (detail.kind === 'duration-over-time') return renderDurationTable(detail.rows, 'bucket');
|
|
1003
|
-
if (detail.kind === 'work-items-over-time') return renderWorkItemsOverTime(detail.rows);
|
|
1004
|
-
if (detail.kind === 'work-item-durations') return renderWorkItemDurations(detail.rows);
|
|
1005
|
-
if (detail.kind === 'work-item-totals') return renderWorkItemTotals(detail.rows);
|
|
1006
|
-
return el('p', { class: 'meta', text: 'Unsupported metric.' });
|
|
1007
|
-
}
|
|
1008
|
-
|
|
1009
|
-
async function renderAnalytics(context) {
|
|
1010
|
-
const metrics = await getJson('/metrics?window=' + encodeURIComponent(analyticsWindow) + '&metric=' + encodeURIComponent(analyticsMetric), context.signal);
|
|
1011
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
1012
|
-
const main = document.getElementById('main');
|
|
1013
|
-
main.replaceChildren(
|
|
1014
|
-
el('div', { class: 'toolbar' }, [
|
|
1015
|
-
el('label', {}, [
|
|
1016
|
-
document.createTextNode('Window'),
|
|
1017
|
-
selectBox(analyticsWindow, [['1d', '1d'], ['3d', '3d'], ['5d', '5d'], ['7d', '7d']], (value) => {
|
|
1018
|
-
analyticsWindow = value;
|
|
1019
|
-
switchView('analytics');
|
|
1020
|
-
}),
|
|
1021
|
-
]),
|
|
1022
|
-
el('label', {}, [
|
|
1023
|
-
document.createTextNode('Metric'),
|
|
1024
|
-
selectBox(analyticsMetric, METRIC_OPTIONS, (value) => {
|
|
1025
|
-
analyticsMetric = value;
|
|
1026
|
-
switchView('analytics');
|
|
1027
|
-
}),
|
|
1028
|
-
]),
|
|
1029
|
-
]),
|
|
1030
|
-
el('div', { class: 'tiles' }, [
|
|
1031
|
-
tile('Runs', fmtNumber(metrics.summary.totalRuns)),
|
|
1032
|
-
tile('Completed', fmtNumber(metrics.summary.completedRuns)),
|
|
1033
|
-
tile('Blocked/approval', fmtNumber(metrics.summary.blockedRuns + metrics.summary.awaitingApprovalRuns)),
|
|
1034
|
-
tile('Failed', fmtNumber(metrics.summary.failedRuns)),
|
|
1035
|
-
tile('Tokens', fmtNumber(metrics.summary.totalTokens)),
|
|
1036
|
-
tile('Cost', fmtCost(metrics.summary.totalCostUsd)),
|
|
1037
|
-
tile('Median run', fmtDuration(metrics.summary.medianRunDurationMs) || '—'),
|
|
1038
|
-
tile('Work done', fmtNumber(metrics.summary.completedWorkItems)),
|
|
1039
|
-
tile('Median e2e', fmtDuration(metrics.summary.medianWorkItemDurationMs) || '—'),
|
|
1040
|
-
]),
|
|
1041
|
-
renderMetricDetail(metrics.detail),
|
|
1042
|
-
);
|
|
1043
|
-
}
|
|
1044
|
-
|
|
1045
|
-
async function renderRuns(context) {
|
|
1046
|
-
const runs = await getJson('/runs', context.signal);
|
|
1047
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
1048
|
-
const main = document.getElementById('main');
|
|
1049
|
-
main.replaceChildren(el('table', {}, [
|
|
1050
|
-
el('tr', {}, ['repo#issue', 'action', 'status', 'sentinel', 'runner', 'tokens', 'cost', 'started', 'finished'].map((h) => el('th', { text: h }))),
|
|
1051
|
-
...runs.map((r) => el('tr', {}, [
|
|
1052
|
-
el('td', { text: r.repo + '#' + r.issueNumber }), el('td', { text: r.action }), el('td', { text: r.status }),
|
|
1053
|
-
el('td', { text: r.sentinel || '' }), el('td', { text: r.routing ? r.routing.runnerName : '' }),
|
|
1054
|
-
el('td', { text: fmtTokens(r.tokenUsage) }), el('td', { text: fmtCost(r.tokenUsage && r.tokenUsage.costUsd) }),
|
|
1055
|
-
el('td', { text: r.startedAt }), el('td', { text: r.finishedAt || '' }),
|
|
1056
|
-
])),
|
|
1057
|
-
]));
|
|
1058
|
-
}
|
|
1059
|
-
|
|
1060
|
-
async function renderConfig(context) {
|
|
1061
|
-
const data = await getJson('/config', context.signal);
|
|
1062
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
1063
|
-
const main = document.getElementById('main');
|
|
1064
|
-
main.replaceChildren(
|
|
1065
|
-
el('h3', { text: 'Routing table' }),
|
|
1066
|
-
el('table', {}, [
|
|
1067
|
-
el('tr', {}, ['stage', 'action', 'tier', 'runner', 'model', 'fallback order'].map((h) => el('th', { text: h }))),
|
|
1068
|
-
...data.routingTable.map((r) => el('tr', {}, [
|
|
1069
|
-
el('td', { text: r.stage }), el('td', { text: r.action || '' }), el('td', { text: r.tier || '' }),
|
|
1070
|
-
el('td', { text: r.runnerName || '' }), el('td', { text: r.model || '' }),
|
|
1071
|
-
el('td', {}, (r.candidates || []).map((c) => {
|
|
1072
|
-
if (!c.paused) return el('span', { class: 'chip', text: c.runnerName });
|
|
1073
|
-
const btn = el('button', { type: 'button', class: 'btn', text: 'Unpause', style: 'margin-top:0;font-size:0.7rem;padding:0.1rem 0.4rem;' });
|
|
1074
|
-
btn.addEventListener('click', async () => {
|
|
1075
|
-
btn.disabled = true;
|
|
1076
|
-
btn.textContent = 'Unpausing…';
|
|
1077
|
-
try {
|
|
1078
|
-
await postJson('/runners/' + encodeURIComponent(c.runnerName) + '/unpause');
|
|
1079
|
-
switchView('config');
|
|
1080
|
-
} catch (err) {
|
|
1081
|
-
btn.disabled = false;
|
|
1082
|
-
btn.textContent = 'Unpause';
|
|
1083
|
-
document.getElementById('status-summary').textContent = 'unpause failed: ' + err.message;
|
|
1084
|
-
}
|
|
1085
|
-
});
|
|
1086
|
-
return el('span', { style: 'display:inline-flex;align-items:center;gap:0.25rem;margin-right:0.25rem;' }, [
|
|
1087
|
-
el('span', { class: 'chip amber', text: c.runnerName + ' (paused)' }),
|
|
1088
|
-
btn,
|
|
1089
|
-
]);
|
|
1090
|
-
})),
|
|
1091
|
-
])),
|
|
1092
|
-
]),
|
|
1093
|
-
el('h3', { text: 'Effective config (redacted)' }),
|
|
1094
|
-
el('pre', { text: JSON.stringify(data.config, null, 2) }),
|
|
1095
|
-
);
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
|
-
async function renderHealth(context) {
|
|
1099
|
-
const health = await getJson('/health', context.signal);
|
|
1100
|
-
if (!isActiveRequest(context.requestId)) return;
|
|
1101
|
-
const main = document.getElementById('main');
|
|
1102
|
-
const runnerNames = Object.keys(health.pause.runnerHealth || {});
|
|
1103
|
-
const runnerSection = runnerNames.length === 0
|
|
1104
|
-
? el('p', { class: 'meta', text: 'No quota failures recorded.' })
|
|
1105
|
-
: el('table', {}, [
|
|
1106
|
-
el('tr', {}, ['runner', 'status', 'paused until', 'failure count', 'last failure'].map((h) => el('th', { text: h }))),
|
|
1107
|
-
...runnerNames.map((name) => {
|
|
1108
|
-
const entry = health.pause.runnerHealth[name];
|
|
1109
|
-
const paused = entry.pausedUntil && Date.parse(entry.pausedUntil) > Date.now();
|
|
1110
|
-
return el('tr', {}, [
|
|
1111
|
-
el('td', { text: name }),
|
|
1112
|
-
el('td', {}, [el('span', { class: 'chip' + (paused ? ' amber' : ' ok') , text: paused ? 'paused' : 'available' })]),
|
|
1113
|
-
el('td', { text: entry.pausedUntil || '' }),
|
|
1114
|
-
el('td', { text: String(entry.failureCount || 0) }),
|
|
1115
|
-
el('td', { text: entry.lastFailureAt || '' }),
|
|
1116
|
-
]);
|
|
1117
|
-
}),
|
|
1118
|
-
]);
|
|
1119
|
-
main.replaceChildren(
|
|
1120
|
-
el('div', { class: 'tiles' }, [
|
|
1121
|
-
tile('Tick lock', health.lock.present ? (health.lock.stale ? 'stale' : 'held') : 'free'),
|
|
1122
|
-
tile('Paused', String(health.pause.paused)),
|
|
1123
|
-
tile('Runners paused now', String(runnerNames.filter((name) => {
|
|
1124
|
-
const until = health.pause.runnerHealth[name].pausedUntil;
|
|
1125
|
-
return until && Date.parse(until) > Date.now();
|
|
1126
|
-
}).length)),
|
|
1127
|
-
tile('Integrity issues', String(health.integrityIssues.length)),
|
|
1128
|
-
]),
|
|
1129
|
-
el('h3', { text: 'Runner health (quota fallback, #67)' }),
|
|
1130
|
-
runnerSection,
|
|
1131
|
-
el('h3', { text: 'Storage' }),
|
|
1132
|
-
el('pre', { text: JSON.stringify(health.storage, null, 2) }),
|
|
1133
|
-
el('h3', { text: 'Source polling' }),
|
|
1134
|
-
el('pre', { text: JSON.stringify(health.sources, null, 2) }),
|
|
1135
|
-
el('h3', { text: 'Integrity issues' }),
|
|
1136
|
-
el('pre', { text: JSON.stringify(health.integrityIssues, null, 2) }),
|
|
1137
|
-
);
|
|
1138
|
-
}
|
|
1139
|
-
|
|
1140
|
-
function tile(label, value) {
|
|
1141
|
-
return el('div', { class: 'tile' }, [el('div', { class: 'n', text: value }), el('div', { class: 'l', text: label })]);
|
|
1142
|
-
}
|
|
1143
|
-
|
|
1144
|
-
const renderers = { board: renderBoard, activity: renderActivity, runs: renderRuns, analytics: renderAnalytics, config: renderConfig, health: renderHealth };
|
|
1145
|
-
|
|
1146
|
-
function switchView(view, options) {
|
|
1147
|
-
if (activeViewRequest) activeViewRequest.controller.abort();
|
|
1148
|
-
const controller = new AbortController();
|
|
1149
|
-
const requestId = ++activeViewRequestId;
|
|
1150
|
-
activeViewRequest = { id: requestId, controller };
|
|
1151
|
-
currentView = view;
|
|
1152
|
-
for (const btn of document.querySelectorAll('nav button')) {
|
|
1153
|
-
btn.classList.toggle('active', btn.dataset.view === view);
|
|
1154
|
-
}
|
|
1155
|
-
const main = document.getElementById('main');
|
|
1156
|
-
if (!options || options.showLoading !== false) {
|
|
1157
|
-
main.replaceChildren(loadingNode(view));
|
|
1158
|
-
}
|
|
1159
|
-
renderers[view]({ signal: controller.signal, requestId }).catch((err) => {
|
|
1160
|
-
if (err.name === 'AbortError' || !isActiveRequest(requestId)) return;
|
|
1161
|
-
main.replaceChildren(el('p', { class: 'meta red', text: 'Failed to load ' + view + ': ' + err.message }));
|
|
1162
|
-
});
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
for (const btn of document.querySelectorAll('nav button')) {
|
|
1166
|
-
btn.addEventListener('click', () => switchView(btn.dataset.view));
|
|
1167
|
-
}
|
|
1168
|
-
document.getElementById('modal-close').addEventListener('click', () => {
|
|
1169
|
-
document.getElementById('modal-overlay').classList.remove('open');
|
|
1170
|
-
});
|
|
1171
|
-
document.getElementById('modal-overlay').addEventListener('click', (event) => {
|
|
1172
|
-
if (event.target.id === 'modal-overlay') {
|
|
1173
|
-
event.currentTarget.classList.remove('open');
|
|
1174
|
-
}
|
|
1175
|
-
});
|
|
1176
|
-
document.getElementById('force-tick').addEventListener('click', forceTickNow);
|
|
1177
|
-
document.getElementById('pause-toggle').addEventListener('click', togglePause);
|
|
1178
|
-
|
|
1179
|
-
renderStatusBar();
|
|
1180
|
-
switchView('board');
|
|
1181
|
-
setInterval(() => {
|
|
1182
|
-
renderStatusBar();
|
|
1183
|
-
switchView(currentView, { showLoading: false });
|
|
1184
|
-
}, 7000);
|
|
1185
|
-
</script>
|
|
1186
|
-
</body>
|
|
1187
|
-
</html>
|
|
1188
|
-
`;
|