code-coordinator 0.5.46__py3-none-any.whl
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.
- code_coordinator-0.5.46.dist-info/METADATA +625 -0
- code_coordinator-0.5.46.dist-info/RECORD +295 -0
- code_coordinator-0.5.46.dist-info/WHEEL +5 -0
- code_coordinator-0.5.46.dist-info/entry_points.txt +2 -0
- code_coordinator-0.5.46.dist-info/licenses/LICENSE +110 -0
- code_coordinator-0.5.46.dist-info/top_level.txt +1 -0
- coord/__init__.py +176 -0
- coord/_board_mapping.py +229 -0
- coord/acceptance.py +468 -0
- coord/acceptance_drivers.py +632 -0
- coord/agent.py +7517 -0
- coord/agent_app.py +1555 -0
- coord/agent_update.py +417 -0
- coord/agents/opencode/.gitignore +13 -0
- coord/agents/opencode/agents/work.md +129 -0
- coord/agents/opencode/routing.jsonc +49 -0
- coord/audit.py +301 -0
- coord/auto_loop.py +1440 -0
- coord/board_bool_guard.py +72 -0
- coord/board_service.py +141 -0
- coord/board_wire.py +309 -0
- coord/brain.py +581 -0
- coord/branch_model.py +214 -0
- coord/cargo_cache.py +258 -0
- coord/ci_github.py +386 -0
- coord/ci_store.py +560 -0
- coord/claim.py +353 -0
- coord/cli.py +454 -0
- coord/client.py +610 -0
- coord/commands/__init__.py +1 -0
- coord/commands/_common.py +329 -0
- coord/commands/acceptance.py +916 -0
- coord/commands/agent_ops.py +1339 -0
- coord/commands/audit.py +131 -0
- coord/commands/chat.py +320 -0
- coord/commands/dispatch.py +1780 -0
- coord/commands/dispatch_workers.py +4894 -0
- coord/commands/drive.py +616 -0
- coord/commands/drive_queue.py +1203 -0
- coord/commands/gate_a.py +217 -0
- coord/commands/gates.py +89 -0
- coord/commands/issues.py +681 -0
- coord/commands/lifecycle.py +513 -0
- coord/commands/merge.py +1900 -0
- coord/commands/milestone.py +2081 -0
- coord/commands/plan_followup.py +1243 -0
- coord/commands/plans.py +156 -0
- coord/commands/release.py +2232 -0
- coord/commands/report.py +341 -0
- coord/commands/review.py +1523 -0
- coord/commands/scorecard.py +252 -0
- coord/commands/sessions.py +1930 -0
- coord/commands/setup.py +576 -0
- coord/commands/status.py +2089 -0
- coord/commands/terminal.py +385 -0
- coord/commands/test_gate.py +775 -0
- coord/commands/tui.py +288 -0
- coord/comments.py +718 -0
- coord/config.py +3032 -0
- coord/conflict_fix.py +633 -0
- coord/dao.py +483 -0
- coord/dashboard/__init__.py +0 -0
- coord/dashboard/fixture.py +376 -0
- coord/dashboard/index.html +658 -0
- coord/dashboard/server.py +1894 -0
- coord/dashboard/terminal.py +382 -0
- coord/dashboard/webapp/.gitignore +9 -0
- coord/dashboard/webapp/components.json +17 -0
- coord/dashboard/webapp/dist/assets/Gallery-da3qNiIw.js +71 -0
- coord/dashboard/webapp/dist/assets/Terminal-9CEnUXvW.css +32 -0
- coord/dashboard/webapp/dist/assets/Terminal-skVFCxPU.js +63 -0
- coord/dashboard/webapp/dist/assets/index-DltfZR5f.js +184 -0
- coord/dashboard/webapp/dist/assets/index-Dq4kwTdw.css +1 -0
- coord/dashboard/webapp/dist/assets/workbox-window.prod.es5-BqEJf4Xk.js +2 -0
- coord/dashboard/webapp/dist/icons/icon-192.png +0 -0
- coord/dashboard/webapp/dist/icons/icon-512.png +0 -0
- coord/dashboard/webapp/dist/icons/icon.svg +5 -0
- coord/dashboard/webapp/dist/index.html +38 -0
- coord/dashboard/webapp/dist/manifest.webmanifest +1 -0
- coord/dashboard/webapp/dist/sw.js +1 -0
- coord/dashboard/webapp/dist/workbox-e4022e15.js +1 -0
- coord/dashboard/webapp/e2e/available-gates-terminal.spec.ts +75 -0
- coord/dashboard/webapp/e2e/deep-link.spec.ts +172 -0
- coord/dashboard/webapp/e2e/fixtureServer.ts +155 -0
- coord/dashboard/webapp/e2e/live-update-fixture.spec.ts +113 -0
- coord/dashboard/webapp/e2e/realtime.spec.ts +238 -0
- coord/dashboard/webapp/e2e/shell.spec.ts +309 -0
- coord/dashboard/webapp/e2e/smoke.spec.ts +191 -0
- coord/dashboard/webapp/e2e/terminal.spec.ts +420 -0
- coord/dashboard/webapp/e2e/theme.spec.ts +138 -0
- coord/dashboard/webapp/eslint.config.js +20 -0
- coord/dashboard/webapp/index.html +37 -0
- coord/dashboard/webapp/node_modules/flatted/python/flatted.py +144 -0
- coord/dashboard/webapp/package-lock.json +10584 -0
- coord/dashboard/webapp/package.json +63 -0
- coord/dashboard/webapp/playwright.acceptance.config.ts +166 -0
- coord/dashboard/webapp/playwright.config.ts +93 -0
- coord/dashboard/webapp/postcss.config.js +6 -0
- coord/dashboard/webapp/public/icons/icon-192.png +0 -0
- coord/dashboard/webapp/public/icons/icon-512.png +0 -0
- coord/dashboard/webapp/public/icons/icon.svg +5 -0
- coord/dashboard/webapp/src/App.tsx +140 -0
- coord/dashboard/webapp/src/api/client.ts +199 -0
- coord/dashboard/webapp/src/api/generated.ts +176 -0
- coord/dashboard/webapp/src/components/ConnectionBadge.tsx +52 -0
- coord/dashboard/webapp/src/components/Detail.tsx +800 -0
- coord/dashboard/webapp/src/components/Gallery.tsx +341 -0
- coord/dashboard/webapp/src/components/Home.tsx +435 -0
- coord/dashboard/webapp/src/components/MobileKeyBar.tsx +280 -0
- coord/dashboard/webapp/src/components/PanelHeader.tsx +59 -0
- coord/dashboard/webapp/src/components/PipelineCard.tsx +168 -0
- coord/dashboard/webapp/src/components/SessionCard.tsx +99 -0
- coord/dashboard/webapp/src/components/SessionDetail.tsx +140 -0
- coord/dashboard/webapp/src/components/SessionsList.tsx +81 -0
- coord/dashboard/webapp/src/components/Terminal.tsx +376 -0
- coord/dashboard/webapp/src/components/__tests__/ConnectionBadge.test.tsx +81 -0
- coord/dashboard/webapp/src/components/__tests__/Detail.test.tsx +680 -0
- coord/dashboard/webapp/src/components/__tests__/Gallery.test.tsx +83 -0
- coord/dashboard/webapp/src/components/__tests__/Home.test.tsx +271 -0
- coord/dashboard/webapp/src/components/__tests__/MobileKeyBar.test.tsx +197 -0
- coord/dashboard/webapp/src/components/__tests__/PipelineCard.test.tsx +143 -0
- coord/dashboard/webapp/src/components/__tests__/SessionCard.test.tsx +106 -0
- coord/dashboard/webapp/src/components/__tests__/Terminal.test.tsx +504 -0
- coord/dashboard/webapp/src/components/ui/badge.tsx +41 -0
- coord/dashboard/webapp/src/components/ui/button.tsx +54 -0
- coord/dashboard/webapp/src/components/ui/card.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/dialog.tsx +99 -0
- coord/dashboard/webapp/src/components/ui/dropdown-menu.tsx +189 -0
- coord/dashboard/webapp/src/components/ui/empty-state.tsx +35 -0
- coord/dashboard/webapp/src/components/ui/sheet.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/skeleton.tsx +9 -0
- coord/dashboard/webapp/src/components/ui/tabs.tsx +55 -0
- coord/dashboard/webapp/src/components/ui/theme-provider.tsx +78 -0
- coord/dashboard/webapp/src/components/ui/theme-toggle.tsx +20 -0
- coord/dashboard/webapp/src/components/ui/toast.tsx +123 -0
- coord/dashboard/webapp/src/components/ui/toaster.tsx +30 -0
- coord/dashboard/webapp/src/components/ui/tooltip.tsx +26 -0
- coord/dashboard/webapp/src/components/ui/use-toast.ts +134 -0
- coord/dashboard/webapp/src/index.css +210 -0
- coord/dashboard/webapp/src/lib/pipeline.ts +29 -0
- coord/dashboard/webapp/src/lib/utils.ts +6 -0
- coord/dashboard/webapp/src/main.tsx +46 -0
- coord/dashboard/webapp/src/realtime/RealtimeProvider.tsx +112 -0
- coord/dashboard/webapp/src/realtime/__tests__/RealtimeProvider.test.tsx +189 -0
- coord/dashboard/webapp/src/realtime/__tests__/connection.test.ts +255 -0
- coord/dashboard/webapp/src/realtime/connection.ts +227 -0
- coord/dashboard/webapp/src/realtime/events.ts +100 -0
- coord/dashboard/webapp/src/routes/__tests__/paths.test.ts +92 -0
- coord/dashboard/webapp/src/routes/paths.ts +92 -0
- coord/dashboard/webapp/src/shell/ActivityRail.tsx +335 -0
- coord/dashboard/webapp/src/shell/AppShell.tsx +276 -0
- coord/dashboard/webapp/src/shell/ComingSoon.tsx +33 -0
- coord/dashboard/webapp/src/shell/EmptyDetail.tsx +26 -0
- coord/dashboard/webapp/src/shell/RouteNotFound.tsx +33 -0
- coord/dashboard/webapp/src/shell/ShellLayout.tsx +147 -0
- coord/dashboard/webapp/src/shell/StatusBar.tsx +46 -0
- coord/dashboard/webapp/src/shell/__tests__/ShellLayout.test.tsx +520 -0
- coord/dashboard/webapp/src/shell/__tests__/shellState.test.ts +95 -0
- coord/dashboard/webapp/src/shell/__tests__/stubViewport.ts +40 -0
- coord/dashboard/webapp/src/shell/breakpoints.ts +87 -0
- coord/dashboard/webapp/src/shell/railItems.ts +105 -0
- coord/dashboard/webapp/src/shell/shellState.ts +174 -0
- coord/dashboard/webapp/src/shell/useRegionFocus.ts +95 -0
- coord/dashboard/webapp/src/test-setup.ts +41 -0
- coord/dashboard/webapp/src/vite-env.d.ts +2 -0
- coord/dashboard/webapp/tailwind.config.js +140 -0
- coord/dashboard/webapp/tsconfig.json +25 -0
- coord/dashboard/webapp/tsconfig.node.json +11 -0
- coord/dashboard/webapp/vite.config.ts +71 -0
- coord/db.py +1076 -0
- coord/dead_end.py +332 -0
- coord/deploy/README.md +33 -0
- coord/deploy/coord-agent.service +89 -0
- coord/deploy/coord-db-backup.service +60 -0
- coord/deploy/coord-db-backup.sh +74 -0
- coord/deploy/coord-db-backup.timer +18 -0
- coord/deploy/coord-drive-queue.service +117 -0
- coord/deploy/coord-drive-queue.timer +39 -0
- coord/deploy/coord-notify.service +48 -0
- coord/deploy/coord-notify.timer +24 -0
- coord/deploy/coord-release-propagate.service +83 -0
- coord/deploy/coord-release-propagate.timer +38 -0
- coord/deploy/coord-release-window.service +119 -0
- coord/deploy/coord-release-window.timer +36 -0
- coord/deploy/coord-serve.service +82 -0
- coord/deploy/coord-web-dist-build.service +43 -0
- coord/deploy/coord-web-dist-build.timer +36 -0
- coord/deploy/coord-web.service +125 -0
- coord/deploy_manifest.py +80 -0
- coord/deploy_units.py +384 -0
- coord/deps.py +115 -0
- coord/diagnose.py +1623 -0
- coord/dispatch.py +1009 -0
- coord/dist_name.py +123 -0
- coord/drive.py +3101 -0
- coord/drive_queue.py +2298 -0
- coord/drive_state.py +870 -0
- coord/events.py +381 -0
- coord/failure_class.py +914 -0
- coord/filelock.py +168 -0
- coord/fleet_config_health.py +300 -0
- coord/freshness.py +206 -0
- coord/gate_a.py +469 -0
- coord/gate_b.py +411 -0
- coord/gate_snapshot.py +385 -0
- coord/gates.py +582 -0
- coord/github_ops.py +1954 -0
- coord/goal.py +125 -0
- coord/graph_health.py +348 -0
- coord/health/__init__.py +69 -0
- coord/health/aggregate.py +129 -0
- coord/health/checks/__init__.py +13 -0
- coord/health/checks/agent_install.py +280 -0
- coord/health/checks/cargo_targets.py +171 -0
- coord/health/checks/claude_binary.py +65 -0
- coord/health/checks/deploy_lane_facts.py +458 -0
- coord/health/checks/disk.py +99 -0
- coord/health/checks/fleet_board.py +89 -0
- coord/health/checks/fleet_deploy_lanes.py +469 -0
- coord/health/checks/fleet_phantom.py +69 -0
- coord/health/checks/fleet_unit_drift.py +151 -0
- coord/health/checks/graph.py +192 -0
- coord/health/checks/plan_usage.py +88 -0
- coord/health/checks/repo_state.py +161 -0
- coord/health/checks/spawned_coord.py +465 -0
- coord/health/checks/timer_active.py +254 -0
- coord/health/checks/toolchain.py +547 -0
- coord/health/checks/unit_drift.py +648 -0
- coord/health/checks/unit_enablement.py +171 -0
- coord/health/checks/worktrees.py +96 -0
- coord/health/cli.py +121 -0
- coord/health/context.py +106 -0
- coord/health/fleet_snapshot.py +477 -0
- coord/health/models.py +250 -0
- coord/health/pypi.py +231 -0
- coord/health/registry.py +240 -0
- coord/health/render.py +82 -0
- coord/health/units.py +60 -0
- coord/hooks.py +106 -0
- coord/housekeeping.py +204 -0
- coord/interactive.py +4286 -0
- coord/issue_store.py +1496 -0
- coord/liveness_auditor.py +293 -0
- coord/machine_pause.py +755 -0
- coord/merge_queue.py +4681 -0
- coord/milestone_chat.py +600 -0
- coord/milestone_dispatch.py +943 -0
- coord/milestone_gate.py +709 -0
- coord/milestone_order.py +840 -0
- coord/mock_author.py +334 -0
- coord/models.py +891 -0
- coord/network.py +269 -0
- coord/new_issue_chat.py +229 -0
- coord/notify.py +3226 -0
- coord/openapi.py +404 -0
- coord/overlap_fence.py +133 -0
- coord/parentage.py +200 -0
- coord/parentage_github.py +58 -0
- coord/pipeline.py +481 -0
- coord/plan_parser.py +266 -0
- coord/plans.py +543 -0
- coord/platform_paths.py +43 -0
- coord/pr_body_lint.py +67 -0
- coord/prereqs.py +533 -0
- coord/progress.py +425 -0
- coord/providers/__init__.py +683 -0
- coord/providers/base.py +218 -0
- coord/providers/claude.py +284 -0
- coord/providers/claude_pty.py +610 -0
- coord/providers/opencode.py +896 -0
- coord/reconcile.py +2233 -0
- coord/refine_chat.py +485 -0
- coord/release_cordon.py +525 -0
- coord/release_propagate.py +1176 -0
- coord/release_verify.py +777 -0
- coord/release_window.py +322 -0
- coord/reports.py +1643 -0
- coord/revalidate.py +1101 -0
- coord/review.py +3317 -0
- coord/scorecard.py +484 -0
- coord/serve_app.py +7192 -0
- coord/skills/update-issue/SKILL.md +93 -0
- coord/smoke.py +1030 -0
- coord/split_work.py +210 -0
- coord/stage_projection.py +650 -0
- coord/state.py +5720 -0
- coord/test_author.py +1064 -0
- coord/test_chat.py +352 -0
- coord/test_orchestrator.py +494 -0
- coord/test_report.py +178 -0
- coord/tui_release.py +271 -0
- coord/usage.py +753 -0
- coord/usage_limits.py +358 -0
- coord/usage_rollup.py +709 -0
- coord/worker_events.py +954 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:var(--font-ui);font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--font-mono);font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}:root{--font-ui: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace;--step--1: .75rem;--step-0: .875rem;--step-1: 1rem;--step-2: 1.25rem;--step-3: 1.5rem;--r-sm: 5px;--r-md: 8px;--r-lg: 12px;--rail-w: 216px;--rail-w-collapsed: 60px;--panel-w: 360px}:root,[data-theme=dark]{--ground: #101418;--surface: #171c22;--surface-2: #1e242b;--surface-3: #252d36;--line: #2a323b;--line-strong: #3a444f;--text: #dfe6ee;--text-dim: #8b97a5;--text-faint: #5f6b78;--accent: #4fb8cf;--accent-dim: #2d7688;--accent-wash: rgba(79, 184, 207, .12);--pass: #4ea87a;--pass-wash: rgba(78, 168, 122, .14);--attn: #d9a54c;--attn-wash: rgba(217, 165, 76, .14);--fail: #d9605e;--fail-wash: rgba(217, 96, 94, .14);--idle: #55606d;--idle-wash: rgba(85, 96, 109, .14);--background: var(--ground);--foreground: var(--text);--card: var(--surface);--card-foreground: var(--text);--popover: var(--surface-2);--popover-foreground: var(--text);--primary: var(--accent);--primary-foreground: #08161a;--secondary: var(--surface-2);--secondary-foreground: var(--text);--muted: var(--surface-2);--muted-foreground: var(--text-dim);--accent-surface: var(--surface-2);--accent-surface-foreground: var(--text);--destructive: var(--fail);--destructive-foreground: #ffffff;--border: var(--line);--input: var(--line);--ring: var(--accent);--radius: var(--r-md)}[data-theme=light]{--ground: #f5f7f9;--surface: #ffffff;--surface-2: #eef1f5;--surface-3: #e3e8ee;--line: #dde3ea;--line-strong: #c3ccd6;--text: #1a212a;--text-dim: #5b6672;--text-faint: #8a95a1;--accent: #1d7f96;--accent-dim: #8fc9d6;--accent-wash: rgba(29, 127, 150, .1);--pass: #2f7d55;--pass-wash: rgba(47, 125, 85, .11);--attn: #a4741d;--attn-wash: rgba(164, 116, 29, .12);--fail: #b8403e;--fail-wash: rgba(184, 64, 62, .11);--idle: #97a1ac;--idle-wash: rgba(151, 161, 172, .14);--background: var(--ground);--foreground: var(--text);--card: var(--surface);--card-foreground: var(--text);--popover: var(--surface-2);--popover-foreground: var(--text);--primary: var(--accent);--primary-foreground: #ffffff;--secondary: var(--surface-2);--secondary-foreground: var(--text);--muted: var(--surface-2);--muted-foreground: var(--text-dim);--accent-surface: var(--surface-2);--accent-surface-foreground: var(--text);--destructive: var(--fail);--destructive-foreground: #ffffff;--border: var(--line);--input: var(--line);--ring: var(--accent)}*{border-color:var(--border)}body{background-color:var(--background);color:var(--foreground);font-family:var(--font-ui);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.mono{font-family:var(--font-mono);font-variant-numeric:tabular-nums}.container{width:100%;margin-right:auto;margin-left:auto;padding-right:2rem;padding-left:2rem}@media (min-width: 640px){.container{max-width:640px}}@media (min-width: 768px){.container{max-width:768px}}@media (min-width: 1024px){.container{max-width:1024px}}@media (min-width: 1280px){.container{max-width:1280px}}@media (min-width: 1536px){.container{max-width:1536px}}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.visible{visibility:visible}.collapse{visibility:collapse}.static{position:static}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.inset-0{top:0;right:0;bottom:0;left:0}.inset-x-0{left:0;right:0}.inset-y-0{top:0;bottom:0}.-left-2{left:-.5rem}.-right-\[3px\]{right:-3px}.bottom-0{bottom:0}.bottom-6{bottom:1.5rem}.left-0{left:0}.left-1\/2{left:50%}.left-2\.5{left:.625rem}.right-0{right:0}.right-2{right:.5rem}.right-4{right:1rem}.top-0{top:0}.top-1\/2{top:50%}.top-2{top:.5rem}.top-4{top:1rem}.z-10{z-index:10}.z-20{z-index:20}.z-40{z-index:40}.z-50{z-index:50}.z-\[100\]{z-index:100}.-mx-\[3px\]{margin-left:-3px;margin-right:-3px}.mx-auto{margin-left:auto;margin-right:auto}.my-1{margin-top:.25rem;margin-bottom:.25rem}.-ml-1{margin-left:-.25rem}.mb-1{margin-bottom:.25rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-5{margin-bottom:1.25rem}.mb-6{margin-bottom:1.5rem}.ml-1{margin-left:.25rem}.ml-auto{margin-left:auto}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.block{display:block}.inline{display:inline}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.contents{display:contents}.hidden{display:none}.h-1\.5{height:.375rem}.h-10{height:2.5rem}.h-2{height:.5rem}.h-3\.5{height:.875rem}.h-4{height:1rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-9{height:2.25rem}.h-\[17px\]{height:17px}.h-\[19px\]{height:19px}.h-\[22px\]{height:22px}.h-\[26px\]{height:26px}.h-\[50vh\]{height:50vh}.h-full{height:100%}.h-px{height:1px}.h-screen{height:100vh}.max-h-64{max-height:16rem}.max-h-screen{max-height:100vh}.min-h-0{min-height:0px}.min-h-screen{min-height:100vh}.w-1{width:.25rem}.w-1\.5{width:.375rem}.w-2{width:.5rem}.w-3\.5{width:.875rem}.w-3\/4{width:75%}.w-32{width:8rem}.w-4{width:1rem}.w-48{width:12rem}.w-7{width:1.75rem}.w-9{width:2.25rem}.w-\[17px\]{width:17px}.w-\[19px\]{width:19px}.w-\[22px\]{width:22px}.w-\[26px\]{width:26px}.w-\[3px\]{width:3px}.w-\[7px\]{width:7px}.w-full{width:100%}.min-w-0{min-width:0px}.min-w-\[10rem\]{min-width:10rem}.min-w-\[246px\]{min-width:246px}.min-w-\[60px\]{min-width:60px}.max-w-3xl{max-width:48rem}.max-w-lg{max-width:32rem}.max-w-sm{max-width:24rem}.max-w-xs{max-width:20rem}.flex-1{flex:1 1 0%}.flex-none{flex:none}.shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-x-1\/2{--tw-translate-x: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.-translate-y-1\/2{--tw-translate-y: -50%;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}.cursor-col-resize{cursor:col-resize}.cursor-default{cursor:default}.cursor-pointer{cursor:pointer}.touch-none{touch-action:none}.select-none{-webkit-user-select:none;-moz-user-select:none;user-select:none}.resize-none{resize:none}.resize{resize:both}.grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.flex-row{flex-direction:row}.flex-col{flex-direction:column}.flex-col-reverse{flex-direction:column-reverse}.flex-wrap{flex-wrap:wrap}.place-items-center{place-items:center}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.gap-0{gap:0px}.gap-0\.5{gap:.125rem}.gap-1{gap:.25rem}.gap-1\.5{gap:.375rem}.gap-10{gap:2.5rem}.gap-2{gap:.5rem}.gap-2\.5{gap:.625rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.gap-\[11px\]{gap:11px}.gap-\[9px\]{gap:9px}.gap-px{gap:1px}.space-y-1\.5>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.375rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.375rem * var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse: 0;margin-top:calc(.75rem * calc(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem * var(--tw-space-y-reverse))}.overflow-hidden{overflow:hidden}.overflow-x-auto{overflow-x:auto}.overflow-y-auto{overflow-y:auto}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-ellipsis{text-overflow:ellipsis}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-wrap{white-space:pre-wrap}.break-words{overflow-wrap:break-word}.rounded{border-radius:.25rem}.rounded-\[7px\]{border-radius:7px}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:var(--r-lg)}.rounded-md{border-radius:var(--r-md)}.rounded-sm{border-radius:var(--r-sm)}.rounded-xl{border-radius:.75rem}.rounded-r-\[3px\]{border-top-right-radius:3px;border-bottom-right-radius:3px}.rounded-t-lg{border-top-left-radius:var(--r-lg);border-top-right-radius:var(--r-lg)}.border{border-width:1px}.border-b{border-bottom-width:1px}.border-b-2{border-bottom-width:2px}.border-l{border-left-width:1px}.border-r{border-right-width:1px}.border-t{border-top-width:1px}.border-attn{border-color:var(--attn)}.border-border{border-color:var(--border)}.border-destructive{border-color:var(--destructive)}.border-fail{border-color:var(--fail)}.border-line-strong{border-color:var(--line-strong)}.border-pass{border-color:var(--pass)}.border-primary{border-color:var(--primary)}.border-transparent{border-color:transparent}.border-yellow-600{--tw-border-opacity: 1;border-color:rgb(202 138 4 / var(--tw-border-opacity, 1))}.bg-\[\#0d1117\]{--tw-bg-opacity: 1;background-color:rgb(13 17 23 / var(--tw-bg-opacity, 1))}.bg-\[\#0d1117\]\/90{background-color:#0d1117e6}.bg-attn-wash{background-color:var(--attn-wash)}.bg-background{background-color:var(--background)}.bg-black\/30{background-color:#0000004d}.bg-black\/60{background-color:#0009}.bg-border{background-color:var(--border)}.bg-brand{background-color:var(--accent)}.bg-brand-wash{background-color:var(--accent-wash)}.bg-card{background-color:var(--card)}.bg-destructive{background-color:var(--destructive)}.bg-fail-wash{background-color:var(--fail-wash)}.bg-green-500{--tw-bg-opacity: 1;background-color:rgb(34 197 94 / var(--tw-bg-opacity, 1))}.bg-green-700{--tw-bg-opacity: 1;background-color:rgb(21 128 61 / var(--tw-bg-opacity, 1))}.bg-muted-foreground{background-color:var(--muted-foreground)}.bg-pass-wash{background-color:var(--pass-wash)}.bg-popover{background-color:var(--popover)}.bg-primary{background-color:var(--primary)}.bg-secondary{background-color:var(--secondary)}.bg-surface{background-color:var(--surface)}.bg-surface-2{background-color:var(--surface-2)}.bg-transparent{background-color:transparent}.bg-yellow-500{--tw-bg-opacity: 1;background-color:rgb(234 179 8 / var(--tw-bg-opacity, 1))}.bg-yellow-600{--tw-bg-opacity: 1;background-color:rgb(202 138 4 / var(--tw-bg-opacity, 1))}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.from-brand{--tw-gradient-from: var(--accent) var(--tw-gradient-from-position);--tw-gradient-to: rgb(255 255 255 / 0) var(--tw-gradient-to-position);--tw-gradient-stops: var(--tw-gradient-from), var(--tw-gradient-to)}.to-brand-dim{--tw-gradient-to: var(--accent-dim) var(--tw-gradient-to-position)}.fill-current{fill:currentColor}.p-0{padding:0}.p-1{padding:.25rem}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-5{padding:1.25rem}.p-6{padding:1.5rem}.p-\[5px\]{padding:5px}.px-0{padding-left:0;padding-right:0}.px-1{padding-left:.25rem;padding-right:.25rem}.px-1\.5{padding-left:.375rem;padding-right:.375rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-2\.5{padding-left:.625rem;padding-right:.625rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-3\.5{padding-left:.875rem;padding-right:.875rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-\[5px\]{padding-left:5px;padding-right:5px}.py-0\.5{padding-top:.125rem;padding-bottom:.125rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-1\.5{padding-top:.375rem;padding-bottom:.375rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-2\.5{padding-top:.625rem;padding-bottom:.625rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-5{padding-top:1.25rem;padding-bottom:1.25rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.pb-1{padding-bottom:.25rem}.pb-24{padding-bottom:6rem}.pb-3{padding-bottom:.75rem}.pb-6{padding-bottom:1.5rem}.pb-8{padding-bottom:2rem}.pb-\[5px\]{padding-bottom:5px}.pb-\[env\(safe-area-inset-bottom\,0px\)\]{padding-bottom:env(safe-area-inset-bottom,0px)}.pl-8{padding-left:2rem}.pr-2\.5{padding-right:.625rem}.pt-0{padding-top:0}.pt-2{padding-top:.5rem}.pt-3\.5{padding-top:.875rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-mono{font-family:var(--font-mono)}.text-\[\.58rem\]{font-size:.58rem}.text-\[\.62rem\]{font-size:.62rem}.text-\[\.66rem\]{font-size:.66rem}.text-\[\.69rem\]{font-size:.69rem}.text-\[\.6rem\]{font-size:.6rem}.text-\[\.72rem\]{font-size:.72rem}.text-\[\.75rem\]{font-size:.75rem}.text-\[\.7rem\]{font-size:.7rem}.text-\[0\.63rem\]{font-size:.63rem}.text-base{font-size:1rem;line-height:1.5rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-step-0{font-size:var(--step-0)}.text-step-1{font-size:var(--step-1)}.text-step-3{font-size:var(--step-3)}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-normal{font-weight:400}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.leading-5{line-height:1.25rem}.leading-none{line-height:1}.leading-tight{line-height:1.25}.tracking-\[-\.01em\]{letter-spacing:-.01em}.tracking-\[-\.02em\]{letter-spacing:-.02em}.tracking-\[\.06em\]{letter-spacing:.06em}.tracking-\[\.09em\]{letter-spacing:.09em}.tracking-tight{letter-spacing:-.025em}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.tracking-widest{letter-spacing:.1em}.text-\[\#0b1013\]{--tw-text-opacity: 1;color:rgb(11 16 19 / var(--tw-text-opacity, 1))}.text-attn{color:var(--attn)}.text-black{--tw-text-opacity: 1;color:rgb(0 0 0 / var(--tw-text-opacity, 1))}.text-blue-400{--tw-text-opacity: 1;color:rgb(96 165 250 / var(--tw-text-opacity, 1))}.text-brand{color:var(--accent)}.text-card-foreground{color:var(--card-foreground)}.text-current{color:currentColor}.text-destructive{color:var(--destructive)}.text-destructive-foreground{color:var(--destructive-foreground)}.text-fail{color:var(--fail)}.text-faint{color:var(--text-faint)}.text-foreground{color:var(--foreground)}.text-green-400{--tw-text-opacity: 1;color:rgb(74 222 128 / var(--tw-text-opacity, 1))}.text-muted-foreground{color:var(--muted-foreground)}.text-pass{color:var(--pass)}.text-popover-foreground{color:var(--popover-foreground)}.text-primary{color:var(--primary)}.text-primary-foreground{color:var(--primary-foreground)}.text-red-400{--tw-text-opacity: 1;color:rgb(248 113 113 / var(--tw-text-opacity, 1))}.text-secondary-foreground{color:var(--secondary-foreground)}.text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity, 1))}.text-yellow-500{--tw-text-opacity: 1;color:rgb(234 179 8 / var(--tw-text-opacity, 1))}.underline-offset-4{text-underline-offset:4px}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-70{opacity:.7}.opacity-90{opacity:.9}.shadow-elevation{--tw-shadow: 0 18px 44px -12px rgba(0,0,0,.66);--tw-shadow-colored: 0 18px 44px -12px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow: 0 10px 15px -3px rgb(0 0 0 / .1), 0 4px 6px -4px rgb(0 0 0 / .1);--tw-shadow-colored: 0 10px 15px -3px var(--tw-shadow-color), 0 4px 6px -4px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-sm{--tw-shadow: 0 1px 2px 0 rgb(0 0 0 / .05);--tw-shadow-colored: 0 1px 2px 0 var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow: 0 20px 25px -5px rgb(0 0 0 / .1), 0 8px 10px -6px rgb(0 0 0 / .1);--tw-shadow-colored: 0 20px 25px -5px var(--tw-shadow-color), 0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.outline-none{outline:2px solid transparent;outline-offset:2px}.outline{outline-style:solid}.ring{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(3px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-1{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.ring-inset{--tw-ring-inset: inset}.ring-ring{--tw-ring-color: var(--ring)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.transition{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke,opacity,box-shadow,transform,filter,backdrop-filter;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-opacity{transition-property:opacity;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.ease-in-out{transition-timing-function:cubic-bezier(.4,0,.2,1)}@keyframes enter{0%{opacity:var(--tw-enter-opacity, 1);transform:translate3d(var(--tw-enter-translate-x, 0),var(--tw-enter-translate-y, 0),0) scale3d(var(--tw-enter-scale, 1),var(--tw-enter-scale, 1),var(--tw-enter-scale, 1)) rotate(var(--tw-enter-rotate, 0))}}@keyframes exit{to{opacity:var(--tw-exit-opacity, 1);transform:translate3d(var(--tw-exit-translate-x, 0),var(--tw-exit-translate-y, 0),0) scale3d(var(--tw-exit-scale, 1),var(--tw-exit-scale, 1),var(--tw-exit-scale, 1)) rotate(var(--tw-exit-rotate, 0))}}.duration-200{animation-duration:.2s}.ease-in-out{animation-timing-function:cubic-bezier(.4,0,.2,1)}.running{animation-play-state:running}.\[height\:100dvh\]{height:100dvh}.placeholder\:text-muted-foreground::-moz-placeholder{color:var(--muted-foreground)}.placeholder\:text-muted-foreground::placeholder{color:var(--muted-foreground)}.before\:absolute:before{content:var(--tw-content);position:absolute}.before\:inset-y-2:before{content:var(--tw-content);top:.5rem;bottom:.5rem}.before\:left-0:before{content:var(--tw-content);left:0}.before\:w-\[2px\]:before{content:var(--tw-content);width:2px}.before\:rounded-r-sm:before{content:var(--tw-content);border-top-right-radius:var(--r-sm);border-bottom-right-radius:var(--r-sm)}.before\:bg-brand:before{content:var(--tw-content);background-color:var(--accent)}.before\:content-\[\\\'\\\'\]:before{--tw-content: \'\';content:var(--tw-content)}.hover\:bg-brand-wash:hover{background-color:var(--accent-wash)}.hover\:bg-secondary:hover{background-color:var(--secondary)}.hover\:bg-surface-2:hover{background-color:var(--surface-2)}.hover\:bg-surface-3:hover{background-color:var(--surface-3)}.hover\:text-attn:hover{color:var(--attn)}.hover\:text-brand:hover{color:var(--accent)}.hover\:text-fail:hover{color:var(--fail)}.hover\:text-foreground:hover{color:var(--foreground)}.hover\:underline:hover{text-decoration-line:underline}.hover\:opacity-100:hover{opacity:1}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-90:hover{opacity:.9}.focus\:bg-secondary:focus{background-color:var(--secondary)}.focus\:text-attn:focus{color:var(--attn)}.focus\:text-fail:focus{color:var(--fail)}.focus\:text-foreground:focus{color:var(--foreground)}.focus\:opacity-100:focus{opacity:1}.focus\:outline-none:focus{outline:2px solid transparent;outline-offset:2px}.focus\:ring-1:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-2:focus{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus\:ring-primary:focus{--tw-ring-color: var(--primary)}.focus\:ring-ring:focus{--tw-ring-color: var(--ring)}.focus\:ring-offset-2:focus{--tw-ring-offset-width: 2px}.focus-visible\:outline-none:focus-visible{outline:2px solid transparent;outline-offset:2px}.focus-visible\:ring-2:focus-visible{--tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow, 0 0 #0000)}.focus-visible\:ring-ring:focus-visible{--tw-ring-color: var(--ring)}.focus-visible\:ring-offset-0:focus-visible{--tw-ring-offset-width: 0px}.focus-visible\:ring-offset-2:focus-visible{--tw-ring-offset-width: 2px}.focus-visible\:ring-offset-background:focus-visible{--tw-ring-offset-color: var(--background)}.active\:bg-accent:active{background-color:var(--accent-surface)}.active\:bg-secondary:active{background-color:var(--secondary)}.active\:opacity-80:active{opacity:.8}.disabled\:pointer-events-none:disabled{pointer-events:none}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:opacity-70{opacity:.7}.data-\[disabled\]\:pointer-events-none[data-disabled]{pointer-events:none}.data-\[swipe\=cancel\]\:translate-x-0[data-swipe=cancel]{--tw-translate-x: 0px;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=end\]\:translate-x-\[var\(--radix-toast-swipe-end-x\)\][data-swipe=end]{--tw-translate-x: var(--radix-toast-swipe-end-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[swipe\=move\]\:translate-x-\[var\(--radix-toast-swipe-move-x\)\][data-swipe=move]{--tw-translate-x: var(--radix-toast-swipe-move-x);transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skew(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.data-\[state\=active\]\:border-primary[data-state=active]{border-color:var(--primary)}.data-\[state\=open\]\:bg-secondary[data-state=open]{background-color:var(--secondary)}.data-\[state\=active\]\:text-foreground[data-state=active],.data-\[state\=open\]\:text-foreground[data-state=open]{color:var(--foreground)}.data-\[disabled\]\:opacity-50[data-disabled]{opacity:.5}.data-\[swipe\=move\]\:transition-none[data-swipe=move]{transition-property:none}.data-\[state\=closed\]\:duration-200[data-state=closed]{transition-duration:.2s}.data-\[state\=open\]\:duration-300[data-state=open]{transition-duration:.3s}.data-\[state\=delayed-open\]\:animate-in[data-state=delayed-open],.data-\[state\=open\]\:animate-in[data-state=open]{animation-name:enter;animation-duration:.15s;--tw-enter-opacity: initial;--tw-enter-scale: initial;--tw-enter-rotate: initial;--tw-enter-translate-x: initial;--tw-enter-translate-y: initial}.data-\[state\=closed\]\:animate-out[data-state=closed],.data-\[swipe\=end\]\:animate-out[data-swipe=end]{animation-name:exit;animation-duration:.15s;--tw-exit-opacity: initial;--tw-exit-scale: initial;--tw-exit-rotate: initial;--tw-exit-translate-x: initial;--tw-exit-translate-y: initial}.data-\[state\=closed\]\:fade-out-0[data-state=closed]{--tw-exit-opacity: 0}.data-\[state\=closed\]\:fade-out-80[data-state=closed]{--tw-exit-opacity: .8}.data-\[state\=delayed-open\]\:fade-in-0[data-state=delayed-open],.data-\[state\=open\]\:fade-in-0[data-state=open]{--tw-enter-opacity: 0}.data-\[state\=closed\]\:zoom-out-95[data-state=closed]{--tw-exit-scale: .95}.data-\[state\=delayed-open\]\:zoom-in-95[data-state=delayed-open],.data-\[state\=open\]\:zoom-in-95[data-state=open]{--tw-enter-scale: .95}.data-\[state\=closed\]\:slide-out-to-bottom[data-state=closed]{--tw-exit-translate-y: 100%}.data-\[state\=closed\]\:slide-out-to-left[data-state=closed]{--tw-exit-translate-x: -100%}.data-\[state\=closed\]\:slide-out-to-right[data-state=closed],.data-\[state\=closed\]\:slide-out-to-right-full[data-state=closed]{--tw-exit-translate-x: 100%}.data-\[state\=closed\]\:slide-out-to-top[data-state=closed]{--tw-exit-translate-y: -100%}.data-\[state\=open\]\:slide-in-from-bottom[data-state=open]{--tw-enter-translate-y: 100%}.data-\[state\=open\]\:slide-in-from-left[data-state=open]{--tw-enter-translate-x: -100%}.data-\[state\=open\]\:slide-in-from-right[data-state=open]{--tw-enter-translate-x: 100%}.data-\[state\=open\]\:slide-in-from-top[data-state=open],.data-\[state\=open\]\:slide-in-from-top-full[data-state=open]{--tw-enter-translate-y: -100%}.data-\[state\=closed\]\:duration-200[data-state=closed]{animation-duration:.2s}.data-\[state\=open\]\:duration-300[data-state=open]{animation-duration:.3s}@media (min-width: 640px){.sm\:bottom-4{bottom:1rem}.sm\:right-4{right:1rem}.sm\:top-auto{top:auto}.sm\:max-w-\[380px\]{max-width:380px}.sm\:max-w-sm{max-width:24rem}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:justify-end{justify-content:flex-end}.sm\:p-10{padding:2.5rem}.data-\[state\=open\]\:sm\:slide-in-from-bottom-full[data-state=open]{--tw-enter-translate-y: 100%}}@media (min-width: 768px){.md\:inline{display:inline}.md\:hidden{display:none}.md\:px-5{padding-left:1.25rem;padding-right:1.25rem}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}}.\[\&_svg\]\:size-4 svg{width:1rem;height:1rem}.\[\&_svg\]\:h-3\.5 svg{height:.875rem}.\[\&_svg\]\:h-8 svg{height:2rem}.\[\&_svg\]\:w-3\.5 svg{width:.875rem}.\[\&_svg\]\:w-8 svg{width:2rem}.\[\&_svg\]\:shrink-0 svg{flex-shrink:0}.\[\&_svg\]\:opacity-80 svg{opacity:.8}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
try{self["workbox:window:7.4.0"]&&_()}catch{}function b(t,r){return new Promise(function(n){var u=new MessageChannel;u.port1.onmessage=function(s){n(s.data)},t.postMessage(r,[u.port2])})}function P(t,r){(r==null||r>t.length)&&(r=t.length);for(var n=0,u=Array(r);n<r;n++)u[n]=t[n];return u}function j(t,r,n){return r&&function(u,s){for(var c=0;c<s.length;c++){var o=s[c];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(u,W(o.key),o)}}(t.prototype,r),Object.defineProperty(t,"prototype",{writable:!1}),t}function S(t,r){var n=typeof Symbol<"u"&&t[Symbol.iterator]||t["@@iterator"];if(n)return(n=n.call(t)).next.bind(n);if(Array.isArray(t)||(n=function(s,c){if(s){if(typeof s=="string")return P(s,c);var o={}.toString.call(s).slice(8,-1);return o==="Object"&&s.constructor&&(o=s.constructor.name),o==="Map"||o==="Set"?Array.from(s):o==="Arguments"||/^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(o)?P(s,c):void 0}}(t))||r){n&&(t=n);var u=0;return function(){return u>=t.length?{done:!0}:{done:!1,value:t[u++]}}}throw new TypeError(`Invalid attempt to iterate non-iterable instance.
|
|
2
|
+
In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function w(t,r){return w=Object.setPrototypeOf?Object.setPrototypeOf.bind():function(n,u){return n.__proto__=u,n},w(t,r)}function W(t){var r=function(n,u){if(typeof n!="object"||!n)return n;var s=n[Symbol.toPrimitive];if(s!==void 0){var c=s.call(n,u);if(typeof c!="object")return c;throw new TypeError("@@toPrimitive must return a primitive value.")}return String(n)}(t,"string");return typeof r=="symbol"?r:r+""}try{self["workbox:core:7.4.0"]&&_()}catch{}var m=function(){var t=this;this.promise=new Promise(function(r,n){t.resolve=r,t.reject=n})};function y(t,r){var n=location.href;return new URL(t,n).href===new URL(r,n).href}var d=function(t,r){this.type=t,Object.assign(this,r)};function l(t,r,n){return t&&t.then||(t=Promise.resolve(t)),r?t.then(r):t}function k(){}var L={type:"SKIP_WAITING"};function E(t,r){return t&&t.then?t.then(k):Promise.resolve()}var O=function(t){function r(c,o){var e,i;return o===void 0&&(o={}),(e=t.call(this)||this).nn={},e.tn=0,e.rn=new m,e.en=new m,e.on=new m,e.un=0,e.an=new Set,e.cn=function(){var f=e.fn,a=f.installing;e.tn>0||!y(a.scriptURL,e.sn.toString())||performance.now()>e.un+6e4?(e.vn=a,f.removeEventListener("updatefound",e.cn)):(e.hn=a,e.an.add(a),e.rn.resolve(a)),++e.tn,a.addEventListener("statechange",e.ln)},e.ln=function(f){var a=e.fn,v=f.target,h=v.state,p=v===e.vn,g={sw:v,isExternal:p,originalEvent:f};!p&&e.mn&&(g.isUpdate=!0),e.dispatchEvent(new d(h,g)),h==="installed"?e.wn=self.setTimeout(function(){h==="installed"&&a.waiting===v&&e.dispatchEvent(new d("waiting",g))},200):h==="activating"&&(clearTimeout(e.wn),p||e.en.resolve(v))},e.yn=function(f){var a=e.hn,v=a!==navigator.serviceWorker.controller;e.dispatchEvent(new d("controlling",{isExternal:v,originalEvent:f,sw:a,isUpdate:e.mn})),v||e.on.resolve(a)},e.gn=(i=function(f){var a=f.data,v=f.ports,h=f.source;return l(e.getSW(),function(){e.an.has(h)&&e.dispatchEvent(new d("message",{data:a,originalEvent:f,ports:v,sw:h}))})},function(){for(var f=[],a=0;a<arguments.length;a++)f[a]=arguments[a];try{return Promise.resolve(i.apply(this,f))}catch(v){return Promise.reject(v)}}),e.sn=c,e.nn=o,navigator.serviceWorker.addEventListener("message",e.gn),e}var n,u;u=t,(n=r).prototype=Object.create(u.prototype),n.prototype.constructor=n,w(n,u);var s=r.prototype;return s.register=function(c){var o=(c===void 0?{}:c).immediate,e=o!==void 0&&o;try{var i=this;return l(function(f,a){var v=f();return v&&v.then?v.then(a):a(v)}(function(){if(!e&&document.readyState!=="complete")return E(new Promise(function(f){return window.addEventListener("load",f)}))},function(){return i.mn=!!navigator.serviceWorker.controller,i.dn=i.pn(),l(i.bn(),function(f){i.fn=f,i.dn&&(i.hn=i.dn,i.en.resolve(i.dn),i.on.resolve(i.dn),i.dn.addEventListener("statechange",i.ln,{once:!0}));var a=i.fn.waiting;return a&&y(a.scriptURL,i.sn.toString())&&(i.hn=a,Promise.resolve().then(function(){i.dispatchEvent(new d("waiting",{sw:a,wasWaitingBeforeRegister:!0}))}).then(function(){})),i.hn&&(i.rn.resolve(i.hn),i.an.add(i.hn)),i.fn.addEventListener("updatefound",i.cn),navigator.serviceWorker.addEventListener("controllerchange",i.yn),i.fn})}))}catch(f){return Promise.reject(f)}},s.update=function(){try{return this.fn?l(E(this.fn.update())):l()}catch(c){return Promise.reject(c)}},s.getSW=function(){return this.hn!==void 0?Promise.resolve(this.hn):this.rn.promise},s.messageSW=function(c){try{return l(this.getSW(),function(o){return b(o,c)})}catch(o){return Promise.reject(o)}},s.messageSkipWaiting=function(){this.fn&&this.fn.waiting&&b(this.fn.waiting,L)},s.pn=function(){var c=navigator.serviceWorker.controller;return c&&y(c.scriptURL,this.sn.toString())?c:void 0},s.bn=function(){try{var c=this;return l(function(o,e){try{var i=o()}catch(f){return e(f)}return i&&i.then?i.then(void 0,e):i}(function(){return l(navigator.serviceWorker.register(c.sn,c.nn),function(o){return c.un=performance.now(),o})},function(o){throw o}))}catch(o){return Promise.reject(o)}},j(r,[{key:"active",get:function(){return this.en.promise}},{key:"controlling",get:function(){return this.on.promise}}])}(function(){function t(){this.Pn=new Map}var r=t.prototype;return r.addEventListener=function(n,u){this.jn(n).add(u)},r.removeEventListener=function(n,u){this.jn(n).delete(u)},r.dispatchEvent=function(n){n.target=this;for(var u,s=S(this.jn(n.type));!(u=s()).done;)(0,u.value)(n)},r.jn=function(n){return this.Pn.has(n)||this.Pn.set(n,new Set),this.Pn.get(n)},t}());export{O as Workbox,d as WorkboxEvent,b as messageSW};
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
|
|
2
|
+
<rect width="512" height="512" rx="80" fill="#1f6feb"/>
|
|
3
|
+
<text x="256" y="358" font-family="monospace,ui-monospace" font-size="320" font-weight="bold"
|
|
4
|
+
fill="white" text-anchor="middle">C</text>
|
|
5
|
+
</svg>
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/icons/icon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
|
7
|
+
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
8
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
9
|
+
<meta name="mobile-web-app-capable" content="yes" />
|
|
10
|
+
<meta name="theme-color" content="#101418" />
|
|
11
|
+
<title>coord dashboard</title>
|
|
12
|
+
<script>
|
|
13
|
+
// Blocking, pre-paint theme resolution (#1546) — must mirror
|
|
14
|
+
// src/components/ui/theme-provider.tsx's `initialTheme()` exactly, or
|
|
15
|
+
// the first React render disagrees with what's already on screen and
|
|
16
|
+
// flashes. Inlined (not a src="") so nothing external delays it.
|
|
17
|
+
(function () {
|
|
18
|
+
try {
|
|
19
|
+
var stored = localStorage.getItem('coord-web-theme');
|
|
20
|
+
var theme =
|
|
21
|
+
stored === 'light' || stored === 'dark'
|
|
22
|
+
? stored
|
|
23
|
+
: window.matchMedia('(prefers-color-scheme: light)').matches
|
|
24
|
+
? 'light'
|
|
25
|
+
: 'dark';
|
|
26
|
+
document.documentElement.setAttribute('data-theme', theme);
|
|
27
|
+
} catch (e) {
|
|
28
|
+
document.documentElement.setAttribute('data-theme', 'dark');
|
|
29
|
+
}
|
|
30
|
+
})();
|
|
31
|
+
</script>
|
|
32
|
+
<script type="module" crossorigin src="/assets/index-DltfZR5f.js"></script>
|
|
33
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Dq4kwTdw.css">
|
|
34
|
+
<link rel="manifest" href="/manifest.webmanifest"></head>
|
|
35
|
+
<body>
|
|
36
|
+
<div id="root"></div>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"coord — dashboard","short_name":"coord","description":"Phone control center for claude-coordinator","start_url":"/","display":"standalone","background_color":"#0d1117","theme_color":"#1f6feb","lang":"en","scope":"/","orientation":"portrait","icons":[{"src":"/icons/icon-192.png","sizes":"192x192","type":"image/png","purpose":"any maskable"},{"src":"/icons/icon-512.png","sizes":"512x512","type":"image/png","purpose":"any maskable"}]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
if(!self.define){let e,i={};const n=(n,s)=>(n=new URL(n+".js",s).href,i[n]||new Promise(i=>{if("document"in self){const e=document.createElement("script");e.src=n,e.onload=i,document.head.appendChild(e)}else e=n,importScripts(n),i()}).then(()=>{let e=i[n];if(!e)throw new Error(`Module ${n} didn’t register its module`);return e}));self.define=(s,c)=>{const o=e||("document"in self?document.currentScript.src:"")||location.href;if(i[o])return;let r={};const l=e=>n(e,o),a={module:{uri:o},exports:r,require:l};i[o]=Promise.all(s.map(e=>a[e]||l(e))).then(e=>(c(...e),r))}}define(["./workbox-e4022e15"],function(e){"use strict";self.skipWaiting(),e.clientsClaim(),e.precacheAndRoute([{url:"manifest.webmanifest",revision:"5b265af79cd849591c2b67aaac52b076"},{url:"index.html",revision:"221f4bf0bb0ce3399f971bf151cf4673"},{url:"icons/icon.svg",revision:"66113aee710ef0e27fa6b916c06820ae"},{url:"icons/icon-512.png",revision:"2d794ce54c2a73ffc813eb12720c8987"},{url:"icons/icon-192.png",revision:"94e9c8f6cc6a571a13700ad89bb57b8f"},{url:"assets/workbox-window.prod.es5-BqEJf4Xk.js",revision:null},{url:"assets/index-Dq4kwTdw.css",revision:null},{url:"assets/index-DltfZR5f.js",revision:null},{url:"assets/Terminal-skVFCxPU.js",revision:null},{url:"assets/Terminal-9CEnUXvW.css",revision:null},{url:"icons/icon-192.png",revision:"94e9c8f6cc6a571a13700ad89bb57b8f"},{url:"icons/icon-512.png",revision:"2d794ce54c2a73ffc813eb12720c8987"},{url:"icons/icon.svg",revision:"66113aee710ef0e27fa6b916c06820ae"},{url:"manifest.webmanifest",revision:"5b265af79cd849591c2b67aaac52b076"}],{}),e.cleanupOutdatedCaches(),e.registerRoute(new e.NavigationRoute(e.createHandlerBoundToURL("index.html"))),e.registerRoute(/^\/api\//,new e.NetworkFirst({cacheName:"api-cache",plugins:[new e.ExpirationPlugin({maxAgeSeconds:60})]}),"GET")});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
define(["exports"],function(t){"use strict";try{self["workbox:core:7.4.0"]&&_()}catch(t){}const e=(t,...e)=>{let s=t;return e.length>0&&(s+=` :: ${JSON.stringify(e)}`),s};class s extends Error{constructor(t,s){super(e(t,s)),this.name=t,this.details=s}}try{self["workbox:routing:7.4.0"]&&_()}catch(t){}const n=t=>t&&"object"==typeof t?t:{handle:t};class i{constructor(t,e,s="GET"){this.handler=n(e),this.match=t,this.method=s}setCatchHandler(t){this.catchHandler=n(t)}}class r extends i{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class a{constructor(){this.t=new Map,this.i=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(e=>{"string"==typeof e&&(e=[e]);const s=new Request(...e);return this.handleRequest({request:s,event:t})}));t.waitUntil(s),t.ports&&t.ports[0]&&s.then(()=>t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;const n=s.origin===location.origin,{params:i,route:r}=this.findMatchingRoute({event:e,request:t,sameOrigin:n,url:s});let a=r&&r.handler;const o=t.method;if(!a&&this.i.has(o)&&(a=this.i.get(o)),!a)return;let c;try{c=a.handle({url:s,request:t,event:e,params:i})}catch(t){c=Promise.reject(t)}const h=r&&r.catchHandler;return c instanceof Promise&&(this.o||h)&&(c=c.catch(async n=>{if(h)try{return await h.handle({url:s,request:t,event:e,params:i})}catch(t){t instanceof Error&&(n=t)}if(this.o)return this.o.handle({url:s,request:t,event:e});throw n})),c}findMatchingRoute({url:t,sameOrigin:e,request:s,event:n}){const i=this.t.get(s.method)||[];for(const r of i){let i;const a=r.match({url:t,sameOrigin:e,request:s,event:n});if(a)return i=a,(Array.isArray(i)&&0===i.length||a.constructor===Object&&0===Object.keys(a).length||"boolean"==typeof a)&&(i=void 0),{route:r,params:i}}return{}}setDefaultHandler(t,e="GET"){this.i.set(e,n(t))}setCatchHandler(t){this.o=n(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new s("unregister-route-but-not-found-with-method",{method:t.method});const e=this.t.get(t.method).indexOf(t);if(!(e>-1))throw new s("unregister-route-route-not-registered");this.t.get(t.method).splice(e,1)}}let o;const c=()=>(o||(o=new a,o.addFetchListener(),o.addCacheListener()),o);function h(t,e,n){let a;if("string"==typeof t){const s=new URL(t,location.href);a=new i(({url:t})=>t.href===s.href,e,n)}else if(t instanceof RegExp)a=new r(t,e,n);else if("function"==typeof t)a=new i(t,e,n);else{if(!(t instanceof i))throw new s("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});a=t}return c().registerRoute(a),a}const u={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},l=t=>[u.prefix,t,u.suffix].filter(t=>t&&t.length>0).join("-"),f=t=>t||l(u.precache),w=t=>t||l(u.runtime);function d(t){t.then(()=>{})}const p=new Set;function y(){return y=Object.assign?Object.assign.bind():function(t){for(var e=1;e<arguments.length;e++){var s=arguments[e];for(var n in s)({}).hasOwnProperty.call(s,n)&&(t[n]=s[n])}return t},y.apply(null,arguments)}let m,g;const R=new WeakMap,v=new WeakMap,b=new WeakMap,q=new WeakMap,D=new WeakMap;let U={get(t,e,s){if(t instanceof IDBTransaction){if("done"===e)return v.get(t);if("objectStoreNames"===e)return t.objectStoreNames||b.get(t);if("store"===e)return s.objectStoreNames[1]?void 0:s.objectStore(s.objectStoreNames[0])}return L(t[e])},set:(t,e,s)=>(t[e]=s,!0),has:(t,e)=>t instanceof IDBTransaction&&("done"===e||"store"===e)||e in t};function x(t){return t!==IDBDatabase.prototype.transaction||"objectStoreNames"in IDBTransaction.prototype?(g||(g=[IDBCursor.prototype.advance,IDBCursor.prototype.continue,IDBCursor.prototype.continuePrimaryKey])).includes(t)?function(...e){return t.apply(E(this),e),L(R.get(this))}:function(...e){return L(t.apply(E(this),e))}:function(e,...s){const n=t.call(E(this),e,...s);return b.set(n,e.sort?e.sort():[e]),L(n)}}function I(t){return"function"==typeof t?x(t):(t instanceof IDBTransaction&&function(t){if(v.has(t))return;const e=new Promise((e,s)=>{const n=()=>{t.removeEventListener("complete",i),t.removeEventListener("error",r),t.removeEventListener("abort",r)},i=()=>{e(),n()},r=()=>{s(t.error||new DOMException("AbortError","AbortError")),n()};t.addEventListener("complete",i),t.addEventListener("error",r),t.addEventListener("abort",r)});v.set(t,e)}(t),e=t,(m||(m=[IDBDatabase,IDBObjectStore,IDBIndex,IDBCursor,IDBTransaction])).some(t=>e instanceof t)?new Proxy(t,U):t);var e}function L(t){if(t instanceof IDBRequest)return function(t){const e=new Promise((e,s)=>{const n=()=>{t.removeEventListener("success",i),t.removeEventListener("error",r)},i=()=>{e(L(t.result)),n()},r=()=>{s(t.error),n()};t.addEventListener("success",i),t.addEventListener("error",r)});return e.then(e=>{e instanceof IDBCursor&&R.set(e,t)}).catch(()=>{}),D.set(e,t),e}(t);if(q.has(t))return q.get(t);const e=I(t);return e!==t&&(q.set(t,e),D.set(e,t)),e}const E=t=>D.get(t);const C=["get","getKey","getAll","getAllKeys","count"],N=["put","add","delete","clear"],O=new Map;function k(t,e){if(!(t instanceof IDBDatabase)||e in t||"string"!=typeof e)return;if(O.get(e))return O.get(e);const s=e.replace(/FromIndex$/,""),n=e!==s,i=N.includes(s);if(!(s in(n?IDBIndex:IDBObjectStore).prototype)||!i&&!C.includes(s))return;const r=async function(t,...e){const r=this.transaction(t,i?"readwrite":"readonly");let a=r.store;return n&&(a=a.index(e.shift())),(await Promise.all([a[s](...e),i&&r.done]))[0]};return O.set(e,r),r}U=(t=>y({},t,{get:(e,s,n)=>k(e,s)||t.get(e,s,n),has:(e,s)=>!!k(e,s)||t.has(e,s)}))(U);try{self["workbox:expiration:7.4.0"]&&_()}catch(t){}const B="cache-entries",T=t=>{const e=new URL(t,location.href);return e.hash="",e.href};class P{constructor(t){this.h=null,this.u=t}l(t){const e=t.createObjectStore(B,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1})}p(t){this.l(t),this.u&&function(t,{blocked:e}={}){const s=indexedDB.deleteDatabase(t);e&&s.addEventListener("blocked",t=>e(t.oldVersion,t)),L(s).then(()=>{})}(this.u)}async setTimestamp(t,e){const s={url:t=T(t),timestamp:e,cacheName:this.u,id:this.m(t)},n=(await this.getDb()).transaction(B,"readwrite",{durability:"relaxed"});await n.store.put(s),await n.done}async getTimestamp(t){const e=await this.getDb(),s=await e.get(B,this.m(t));return null==s?void 0:s.timestamp}async expireEntries(t,e){const s=await this.getDb();let n=await s.transaction(B).store.index("timestamp").openCursor(null,"prev");const i=[];let r=0;for(;n;){const s=n.value;s.cacheName===this.u&&(t&&s.timestamp<t||e&&r>=e?i.push(n.value):r++),n=await n.continue()}const a=[];for(const t of i)await s.delete(B,t.id),a.push(t.url);return a}m(t){return this.u+"|"+T(t)}async getDb(){return this.h||(this.h=await function(t,e,{blocked:s,upgrade:n,blocking:i,terminated:r}={}){const a=indexedDB.open(t,e),o=L(a);return n&&a.addEventListener("upgradeneeded",t=>{n(L(a.result),t.oldVersion,t.newVersion,L(a.transaction),t)}),s&&a.addEventListener("blocked",t=>s(t.oldVersion,t.newVersion,t)),o.then(t=>{r&&t.addEventListener("close",()=>r()),i&&t.addEventListener("versionchange",t=>i(t.oldVersion,t.newVersion,t))}).catch(()=>{}),o}("workbox-expiration",1,{upgrade:this.p.bind(this)})),this.h}}class M{constructor(t,e={}){this.R=!1,this.v=!1,this.q=e.maxEntries,this.D=e.maxAgeSeconds,this.U=e.matchOptions,this.u=t,this._=new P(t)}async expireEntries(){if(this.R)return void(this.v=!0);this.R=!0;const t=this.D?Date.now()-1e3*this.D:0,e=await this._.expireEntries(t,this.q),s=await self.caches.open(this.u);for(const t of e)await s.delete(t,this.U);this.R=!1,this.v&&(this.v=!1,d(this.expireEntries()))}async updateTimestamp(t){await this._.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.D){const e=await this._.getTimestamp(t),s=Date.now()-1e3*this.D;return void 0===e||e<s}return!1}async delete(){this.v=!1,await this._.expireEntries(1/0)}}try{self["workbox:strategies:7.4.0"]&&_()}catch(t){}const W={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null};function j(t,e){const s=new URL(t);for(const t of e)s.searchParams.delete(t);return s.href}class S{constructor(){this.promise=new Promise((t,e)=>{this.resolve=t,this.reject=e})}}function K(t){return"string"==typeof t?new Request(t):t}class A{constructor(t,e){this.I={},Object.assign(this,e),this.event=e.event,this.L=t,this.C=new S,this.N=[],this.O=[...t.plugins],this.k=new Map;for(const t of this.O)this.k.set(t,{});this.event.waitUntil(this.C.promise)}async fetch(t){const{event:e}=this;let n=K(t);if("navigate"===n.mode&&e instanceof FetchEvent&&e.preloadResponse){const t=await e.preloadResponse;if(t)return t}const i=this.hasCallback("fetchDidFail")?n.clone():null;try{for(const t of this.iterateCallbacks("requestWillFetch"))n=await t({request:n.clone(),event:e})}catch(t){if(t instanceof Error)throw new s("plugin-error-request-will-fetch",{thrownErrorMessage:t.message})}const r=n.clone();try{let t;t=await fetch(n,"navigate"===n.mode?void 0:this.L.fetchOptions);for(const s of this.iterateCallbacks("fetchDidSucceed"))t=await s({event:e,request:r,response:t});return t}catch(t){throw i&&await this.runCallbacks("fetchDidFail",{error:t,event:e,originalRequest:i.clone(),request:r.clone()}),t}}async fetchAndCachePut(t){const e=await this.fetch(t),s=e.clone();return this.waitUntil(this.cachePut(t,s)),e}async cacheMatch(t){const e=K(t);let s;const{cacheName:n,matchOptions:i}=this.L,r=await this.getCacheKey(e,"read"),a=Object.assign(Object.assign({},i),{cacheName:n});s=await caches.match(r,a);for(const t of this.iterateCallbacks("cachedResponseWillBeUsed"))s=await t({cacheName:n,matchOptions:i,cachedResponse:s,request:r,event:this.event})||void 0;return s}async cachePut(t,e){const n=K(t);var i;await(i=0,new Promise(t=>setTimeout(t,i)));const r=await this.getCacheKey(n,"write");if(!e)throw new s("cache-put-with-no-response",{url:(a=r.url,new URL(String(a),location.href).href.replace(new RegExp(`^${location.origin}`),""))});var a;const o=await this.B(e);if(!o)return!1;const{cacheName:c,matchOptions:h}=this.L,u=await self.caches.open(c),l=this.hasCallback("cacheDidUpdate"),f=l?await async function(t,e,s,n){const i=j(e.url,s);if(e.url===i)return t.match(e,n);const r=Object.assign(Object.assign({},n),{ignoreSearch:!0}),a=await t.keys(e,r);for(const e of a)if(i===j(e.url,s))return t.match(e,n)}(u,r.clone(),["__WB_REVISION__"],h):null;try{await u.put(r,l?o.clone():o)}catch(t){if(t instanceof Error)throw"QuotaExceededError"===t.name&&await async function(){for(const t of p)await t()}(),t}for(const t of this.iterateCallbacks("cacheDidUpdate"))await t({cacheName:c,oldResponse:f,newResponse:o.clone(),request:r,event:this.event});return!0}async getCacheKey(t,e){const s=`${t.url} | ${e}`;if(!this.I[s]){let n=t;for(const t of this.iterateCallbacks("cacheKeyWillBeUsed"))n=K(await t({mode:e,request:n,event:this.event,params:this.params}));this.I[s]=n}return this.I[s]}hasCallback(t){for(const e of this.L.plugins)if(t in e)return!0;return!1}async runCallbacks(t,e){for(const s of this.iterateCallbacks(t))await s(e)}*iterateCallbacks(t){for(const e of this.L.plugins)if("function"==typeof e[t]){const s=this.k.get(e),n=n=>{const i=Object.assign(Object.assign({},n),{state:s});return e[t](i)};yield n}}waitUntil(t){return this.N.push(t),t}async doneWaiting(){for(;this.N.length;){const t=this.N.splice(0),e=(await Promise.allSettled(t)).find(t=>"rejected"===t.status);if(e)throw e.reason}}destroy(){this.C.resolve(null)}async B(t){let e=t,s=!1;for(const t of this.iterateCallbacks("cacheWillUpdate"))if(e=await t({request:this.request,response:e,event:this.event})||void 0,s=!0,!e)break;return s||e&&200!==e.status&&(e=void 0),e}}class F{constructor(t={}){this.cacheName=w(t.cacheName),this.plugins=t.plugins||[],this.fetchOptions=t.fetchOptions,this.matchOptions=t.matchOptions}handle(t){const[e]=this.handleAll(t);return e}handleAll(t){t instanceof FetchEvent&&(t={event:t,request:t.request});const e=t.event,s="string"==typeof t.request?new Request(t.request):t.request,n="params"in t?t.params:void 0,i=new A(this,{event:e,request:s,params:n}),r=this.T(i,s,e);return[r,this.P(r,i,s,e)]}async T(t,e,n){let i;await t.runCallbacks("handlerWillStart",{event:n,request:e});try{if(i=await this.M(e,t),!i||"error"===i.type)throw new s("no-response",{url:e.url})}catch(s){if(s instanceof Error)for(const r of t.iterateCallbacks("handlerDidError"))if(i=await r({error:s,event:n,request:e}),i)break;if(!i)throw s}for(const s of t.iterateCallbacks("handlerWillRespond"))i=await s({event:n,request:e,response:i});return i}async P(t,e,s,n){let i,r;try{i=await t}catch(r){}try{await e.runCallbacks("handlerDidRespond",{event:n,request:s,response:i}),await e.doneWaiting()}catch(t){t instanceof Error&&(r=t)}if(await e.runCallbacks("handlerDidComplete",{event:n,request:s,response:i,error:r}),e.destroy(),r)throw r}}function H(t,e){const s=e();return t.waitUntil(s),s}try{self["workbox:precaching:7.4.0"]&&_()}catch(t){}function $(t){if(!t)throw new s("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new s("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const i=new URL(n,location.href),r=new URL(n,location.href);return i.searchParams.set("__WB_REVISION__",e),{cacheKey:i.href,url:r.href}}class G{constructor(){this.updatedURLs=[],this.notUpdatedURLs=[],this.handlerWillStart=async({request:t,state:e})=>{e&&(e.originalRequest=t)},this.cachedResponseWillBeUsed=async({event:t,state:e,cachedResponse:s})=>{if("install"===t.type&&e&&e.originalRequest&&e.originalRequest instanceof Request){const t=e.originalRequest.url;s?this.notUpdatedURLs.push(t):this.updatedURLs.push(t)}return s}}}class V{constructor({precacheController:t}){this.cacheKeyWillBeUsed=async({request:t,params:e})=>{const s=(null==e?void 0:e.cacheKey)||this.W.getCacheKeyForURL(t.url);return s?new Request(s,{headers:t.headers}):t},this.W=t}}let J,Q;async function z(t,e){let n=null;if(t.url){n=new URL(t.url).origin}if(n!==self.location.origin)throw new s("cross-origin-copy-response",{origin:n});const i=t.clone(),r={headers:new Headers(i.headers),status:i.status,statusText:i.statusText},a=function(){if(void 0===J){const t=new Response("");if("body"in t)try{new Response(t.body),J=!0}catch(t){J=!1}J=!1}return J}()?i.body:await i.blob();return new Response(a,r)}class X extends F{constructor(t={}){t.cacheName=f(t.cacheName),super(t),this.j=!1!==t.fallbackToNetwork,this.plugins.push(X.copyRedirectedCacheableResponsesPlugin)}async M(t,e){const s=await e.cacheMatch(t);return s||(e.event&&"install"===e.event.type?await this.S(t,e):await this.K(t,e))}async K(t,e){let n;const i=e.params||{};if(!this.j)throw new s("missing-precache-entry",{cacheName:this.cacheName,url:t.url});{const s=i.integrity,r=t.integrity,a=!r||r===s;n=await e.fetch(new Request(t,{integrity:"no-cors"!==t.mode?r||s:void 0})),s&&a&&"no-cors"!==t.mode&&(this.A(),await e.cachePut(t,n.clone()))}return n}async S(t,e){this.A();const n=await e.fetch(t);if(!await e.cachePut(t,n.clone()))throw new s("bad-precaching-response",{url:t.url,status:n.status});return n}A(){let t=null,e=0;for(const[s,n]of this.plugins.entries())n!==X.copyRedirectedCacheableResponsesPlugin&&(n===X.defaultPrecacheCacheabilityPlugin&&(t=s),n.cacheWillUpdate&&e++);0===e?this.plugins.push(X.defaultPrecacheCacheabilityPlugin):e>1&&null!==t&&this.plugins.splice(t,1)}}X.defaultPrecacheCacheabilityPlugin={cacheWillUpdate:async({response:t})=>!t||t.status>=400?null:t},X.copyRedirectedCacheableResponsesPlugin={cacheWillUpdate:async({response:t})=>t.redirected?await z(t):t};class Y{constructor({cacheName:t,plugins:e=[],fallbackToNetwork:s=!0}={}){this.F=new Map,this.H=new Map,this.$=new Map,this.L=new X({cacheName:f(t),plugins:[...e,new V({precacheController:this})],fallbackToNetwork:s}),this.install=this.install.bind(this),this.activate=this.activate.bind(this)}get strategy(){return this.L}precache(t){this.addToCacheList(t),this.G||(self.addEventListener("install",this.install),self.addEventListener("activate",this.activate),this.G=!0)}addToCacheList(t){const e=[];for(const n of t){"string"==typeof n?e.push(n):n&&void 0===n.revision&&e.push(n.url);const{cacheKey:t,url:i}=$(n),r="string"!=typeof n&&n.revision?"reload":"default";if(this.F.has(i)&&this.F.get(i)!==t)throw new s("add-to-cache-list-conflicting-entries",{firstEntry:this.F.get(i),secondEntry:t});if("string"!=typeof n&&n.integrity){if(this.$.has(t)&&this.$.get(t)!==n.integrity)throw new s("add-to-cache-list-conflicting-integrities",{url:i});this.$.set(t,n.integrity)}if(this.F.set(i,t),this.H.set(i,r),e.length>0){const t=`Workbox is precaching URLs without revision info: ${e.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;console.warn(t)}}}install(t){return H(t,async()=>{const e=new G;this.strategy.plugins.push(e);for(const[e,s]of this.F){const n=this.$.get(s),i=this.H.get(e),r=new Request(e,{integrity:n,cache:i,credentials:"same-origin"});await Promise.all(this.strategy.handleAll({params:{cacheKey:s},request:r,event:t}))}const{updatedURLs:s,notUpdatedURLs:n}=e;return{updatedURLs:s,notUpdatedURLs:n}})}activate(t){return H(t,async()=>{const t=await self.caches.open(this.strategy.cacheName),e=await t.keys(),s=new Set(this.F.values()),n=[];for(const i of e)s.has(i.url)||(await t.delete(i),n.push(i.url));return{deletedURLs:n}})}getURLsToCacheKeys(){return this.F}getCachedURLs(){return[...this.F.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.F.get(e.href)}getIntegrityForCacheKey(t){return this.$.get(t)}async matchPrecache(t){const e=t instanceof Request?t.url:t,s=this.getCacheKeyForURL(e);if(s){return(await self.caches.open(this.strategy.cacheName)).match(s)}}createHandlerBoundToURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new s("non-precached-url",{url:t});return s=>(s.request=new Request(t),s.params=Object.assign({cacheKey:e},s.params),this.strategy.handle(s))}}const Z=()=>(Q||(Q=new Y),Q);class tt extends i{constructor(t,e){super(({request:s})=>{const n=t.getURLsToCacheKeys();for(const i of function*(t,{ignoreURLParametersMatching:e=[/^utm_/,/^fbclid$/],directoryIndex:s="index.html",cleanURLs:n=!0,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const a=function(t,e=[]){for(const s of[...t.searchParams.keys()])e.some(t=>t.test(s))&&t.searchParams.delete(s);return t}(r,e);if(yield a.href,s&&a.pathname.endsWith("/")){const t=new URL(a.href);t.pathname+=s,yield t.href}if(n){const t=new URL(a.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(s.url,e)){const e=n.get(i);if(e){return{cacheKey:e,integrity:t.getIntegrityForCacheKey(e)}}}},t.strategy)}}t.ExpirationPlugin=class{constructor(t={}){this.cachedResponseWillBeUsed=async({event:t,request:e,cacheName:s,cachedResponse:n})=>{if(!n)return null;const i=this.V(n),r=this.J(s);d(r.expireEntries());const a=r.updateTimestamp(e.url);if(t)try{t.waitUntil(a)}catch(t){}return i?n:null},this.cacheDidUpdate=async({cacheName:t,request:e})=>{const s=this.J(t);await s.updateTimestamp(e.url),await s.expireEntries()},this.X=t,this.D=t.maxAgeSeconds,this.Y=new Map,t.purgeOnQuotaError&&function(t){p.add(t)}(()=>this.deleteCacheAndMetadata())}J(t){if(t===w())throw new s("expire-custom-caches-only");let e=this.Y.get(t);return e||(e=new M(t,this.X),this.Y.set(t,e)),e}V(t){if(!this.D)return!0;const e=this.Z(t);if(null===e)return!0;return e>=Date.now()-1e3*this.D}Z(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async deleteCacheAndMetadata(){for(const[t,e]of this.Y)await self.caches.delete(t),await e.delete();this.Y=new Map}},t.NavigationRoute=class extends i{constructor(t,{allowlist:e=[/./],denylist:s=[]}={}){super(t=>this.tt(t),t),this.et=e,this.st=s}tt({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.st)if(t.test(s))return!1;return!!this.et.some(t=>t.test(s))}},t.NetworkFirst=class extends F{constructor(t={}){super(t),this.plugins.some(t=>"cacheWillUpdate"in t)||this.plugins.unshift(W),this.nt=t.networkTimeoutSeconds||0}async M(t,e){const n=[],i=[];let r;if(this.nt){const{id:s,promise:a}=this.it({request:t,logs:n,handler:e});r=s,i.push(a)}const a=this.rt({timeoutId:r,request:t,logs:n,handler:e});i.push(a);const o=await e.waitUntil((async()=>await e.waitUntil(Promise.race(i))||await a)());if(!o)throw new s("no-response",{url:t.url});return o}it({request:t,logs:e,handler:s}){let n;return{promise:new Promise(e=>{n=setTimeout(async()=>{e(await s.cacheMatch(t))},1e3*this.nt)}),id:n}}async rt({timeoutId:t,request:e,logs:s,handler:n}){let i,r;try{r=await n.fetchAndCachePut(e)}catch(t){t instanceof Error&&(i=t)}return t&&clearTimeout(t),!i&&r||(r=await n.cacheMatch(e)),r}},t.cleanupOutdatedCaches=function(){self.addEventListener("activate",t=>{const e=f();t.waitUntil((async(t,e="-precache-")=>{const s=(await self.caches.keys()).filter(s=>s.includes(e)&&s.includes(self.registration.scope)&&s!==t);return await Promise.all(s.map(t=>self.caches.delete(t))),s})(e).then(t=>{}))})},t.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},t.createHandlerBoundToURL=function(t){return Z().createHandlerBoundToURL(t)},t.precacheAndRoute=function(t,e){!function(t){Z().precache(t)}(t),function(t){const e=Z();h(new tt(e,t))}(e)},t.registerRoute=h});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #2084 — terminal assignments must not inflate the "Needs me" badge.
|
|
3
|
+
*
|
|
4
|
+
* `coord/pipeline.py`'s gate projection used to offer "Dispatch
|
|
5
|
+
* Review"/"Queue for Merge"/"Record Test Verdict" on assignments that had
|
|
6
|
+
* already finished the whole pipeline — chiefly `status == "merged"` rows
|
|
7
|
+
* `coord.reconcile`'s GitHub-truth sweep (`work_is_terminal`) sets
|
|
8
|
+
* independently of the merge queue, which `compute_pipeline` had no branch
|
|
9
|
+
* for and silently treated as indistinguishable from a work item that had
|
|
10
|
+
* never been dispatched anywhere. On the live board this inflated
|
|
11
|
+
* `available_gates` (and therefore the webapp's "Needs me" badge, per
|
|
12
|
+
* `src/lib/pipeline.ts`'s `needsMe = available_gates.length > 0`) to nearly
|
|
13
|
+
* the size of the whole board.
|
|
14
|
+
*
|
|
15
|
+
* This is the black-box regression CLAUDE.md's acceptance bar asks for: a
|
|
16
|
+
* REAL `coord web --fixture` process (not a `page.route()` intercept) runs
|
|
17
|
+
* the actual `compute_pipeline` server-side against a seeded board mixing
|
|
18
|
+
* genuinely-terminal assignments with one genuinely-live one, and the
|
|
19
|
+
* assertions read the rendered "Needs me"/"Active" tab badges — the same
|
|
20
|
+
* counts an operator sees. `tests/test_pipeline.py` covers the unit-level
|
|
21
|
+
* half of the fix; `tests/fixtures/board-pipeline-terminal-gates.json` is
|
|
22
|
+
* this spec's fixture (see that file's header for the three seeded rows).
|
|
23
|
+
*
|
|
24
|
+
* Run: npm run test:e2e (requires `coord` on $PATH, see fixtureServer.ts)
|
|
25
|
+
*/
|
|
26
|
+
import { test, expect } from '@playwright/test'
|
|
27
|
+
import path from 'node:path'
|
|
28
|
+
import { startFixtureServer, REPO_ROOT, type FixtureServerHandle } from './fixtureServer'
|
|
29
|
+
|
|
30
|
+
const FIXTURE_PATH = path.join(REPO_ROOT, 'tests/fixtures/board-pipeline-terminal-gates.json')
|
|
31
|
+
|
|
32
|
+
test.describe('terminal assignments offer no gates (#2084)', () => {
|
|
33
|
+
let server: FixtureServerHandle
|
|
34
|
+
|
|
35
|
+
test.beforeAll(async () => {
|
|
36
|
+
server = await startFixtureServer(FIXTURE_PATH)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test.afterAll(async () => {
|
|
40
|
+
await server?.stop()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('Needs me counts only the genuinely-actionable row, not the merged/advisory ones', async ({
|
|
44
|
+
page,
|
|
45
|
+
}) => {
|
|
46
|
+
await page.goto(server.baseUrl)
|
|
47
|
+
|
|
48
|
+
// Sanity: the real dist bundle booted against the real fixture data. The
|
|
49
|
+
// Active tab (selected on load) collapses both "done" rows (#5201,
|
|
50
|
+
// #5203 — neither is "merged") into the "Work done" section (#1218), so
|
|
51
|
+
// assert on that collapsed count rather than a card's visibility.
|
|
52
|
+
await expect(page.getByRole('button', { name: 'Work done (2)' })).toBeVisible()
|
|
53
|
+
|
|
54
|
+
// "Needs me": only work-needs-me (#5201) has a genuinely-offerable gate.
|
|
55
|
+
// work-merged-direct (#5202, status="merged" with no merge_queue entry —
|
|
56
|
+
// the #2084 repro) and work-advisory (#5203, status="advisory") must
|
|
57
|
+
// both read zero.
|
|
58
|
+
const needsMeTab = page.getByRole('tab', { name: /^Needs me/ })
|
|
59
|
+
await expect(needsMeTab).toHaveText('Needs me1')
|
|
60
|
+
|
|
61
|
+
await needsMeTab.click()
|
|
62
|
+
const list = page.getByRole('region', { name: 'Items needing attention' })
|
|
63
|
+
await expect(list.getByText('#5201')).toBeVisible()
|
|
64
|
+
await expect(list.getByText('#5202')).toHaveCount(0)
|
|
65
|
+
await expect(list.getByText('#5203')).toHaveCount(0)
|
|
66
|
+
|
|
67
|
+
// "Active": current_stage != "merged" — work-merged-direct (#5202) is
|
|
68
|
+
// correctly excluded (it finished the whole pipeline), while
|
|
69
|
+
// work-advisory (#5203, current_stage stays "done" — nothing to gate,
|
|
70
|
+
// but not yet aged/settled either) still counts as in-flight alongside
|
|
71
|
+
// work-needs-me (#5201). Two, not three.
|
|
72
|
+
const activeTab = page.getByRole('tab', { name: /^Active/ })
|
|
73
|
+
await expect(activeTab).toHaveText('Active2')
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deep-link cold load + the phone regression net (#1551, M-W1's exit gate).
|
|
3
|
+
*
|
|
4
|
+
* "Cold load" means the FIRST navigation Playwright makes is the deep link
|
|
5
|
+
* itself (`page.goto('/pipeline/api/42')`) -- never Home first, then a click.
|
|
6
|
+
* `shell.spec.ts`'s "the selected view survives a reload" gets partway there
|
|
7
|
+
* (a `reload()` re-runs the app's URL -> view resolution) but only for the
|
|
8
|
+
* Sessions view, and only at the wide viewport. This file is the direct
|
|
9
|
+
* `page.goto(deepPath)` version, at both breakpoints, for both a rail view
|
|
10
|
+
* and a pipeline item's detail route -- the two `shellViewFromPath` /
|
|
11
|
+
* `paths.pipelineItem` shapes `routes/paths.ts` (#1548) exists to make
|
|
12
|
+
* addressable.
|
|
13
|
+
*
|
|
14
|
+
* The phone regression net closes this milestone's other named criterion:
|
|
15
|
+
* "the flows that exist today still work at narrow width." `smoke.spec.ts`
|
|
16
|
+
* (#741) is that net's primary home and stays exactly as it is; the test
|
|
17
|
+
* below is a deliberately cheap, single end-to-end chain through the same
|
|
18
|
+
* three flows (render -> filter -> drill in -> back), run here so this
|
|
19
|
+
* milestone's own file carries a self-contained assertion of it rather than
|
|
20
|
+
* only trusting a cross-file reference.
|
|
21
|
+
*
|
|
22
|
+
* Runs at both breakpoints as distinct Playwright projects ('wide' /
|
|
23
|
+
* 'narrow', see playwright.config.ts).
|
|
24
|
+
*
|
|
25
|
+
* Run: npm run test:e2e
|
|
26
|
+
*/
|
|
27
|
+
import { test, expect, type Page } from '@playwright/test'
|
|
28
|
+
|
|
29
|
+
const SEEDED_PIPELINE = [
|
|
30
|
+
{
|
|
31
|
+
assignment_id: 'work-1',
|
|
32
|
+
issue_number: 42,
|
|
33
|
+
issue_title: 'Fix the dashboard rendering',
|
|
34
|
+
repo_name: 'api',
|
|
35
|
+
machine_name: 'laptop',
|
|
36
|
+
current_stage: 'coding',
|
|
37
|
+
stages: [
|
|
38
|
+
{ name: 'coding', status: 'active', is_current: true },
|
|
39
|
+
{ name: 'review', status: 'waiting', is_current: false },
|
|
40
|
+
{ name: 'merge', status: 'waiting', is_current: false },
|
|
41
|
+
],
|
|
42
|
+
available_gates: [],
|
|
43
|
+
progress_pct: 20,
|
|
44
|
+
review_findings_pending: false,
|
|
45
|
+
review_verdict: null,
|
|
46
|
+
review_findings_body: null,
|
|
47
|
+
test_verdict: null,
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
assignment_id: 'work-2',
|
|
51
|
+
issue_number: 99,
|
|
52
|
+
issue_title: 'Refactor merge queue',
|
|
53
|
+
repo_name: 'api',
|
|
54
|
+
machine_name: 'server',
|
|
55
|
+
current_stage: 'review_running',
|
|
56
|
+
stages: [
|
|
57
|
+
{ name: 'coding', status: 'completed', is_current: false },
|
|
58
|
+
{ name: 'review', status: 'active', is_current: true },
|
|
59
|
+
{ name: 'merge', status: 'waiting', is_current: false },
|
|
60
|
+
],
|
|
61
|
+
available_gates: [],
|
|
62
|
+
progress_pct: 60,
|
|
63
|
+
review_findings_pending: false,
|
|
64
|
+
review_verdict: null,
|
|
65
|
+
review_findings_body: null,
|
|
66
|
+
test_verdict: null,
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
|
|
70
|
+
const SEEDED_SESSIONS = [
|
|
71
|
+
{
|
|
72
|
+
session_id: 'sess-1',
|
|
73
|
+
session_name: 'coord-sess-1',
|
|
74
|
+
machine: 'dellserver',
|
|
75
|
+
host: 'dellserver.local',
|
|
76
|
+
repo: 'api',
|
|
77
|
+
issue: 7,
|
|
78
|
+
issue_title: 'Live session takeover',
|
|
79
|
+
stage: 'work',
|
|
80
|
+
status: 'running',
|
|
81
|
+
attached: false,
|
|
82
|
+
pane_dead: false,
|
|
83
|
+
},
|
|
84
|
+
]
|
|
85
|
+
|
|
86
|
+
async function mockApi(page: Page): Promise<void> {
|
|
87
|
+
await page.route('**/api/pipeline', (route) =>
|
|
88
|
+
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(SEEDED_PIPELINE) }),
|
|
89
|
+
)
|
|
90
|
+
await page.route('**/api/sessions', (route) =>
|
|
91
|
+
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(SEEDED_SESSIONS) }),
|
|
92
|
+
)
|
|
93
|
+
await page.route('**/api/board', (route) =>
|
|
94
|
+
route.fulfill({
|
|
95
|
+
status: 200,
|
|
96
|
+
contentType: 'application/json',
|
|
97
|
+
body: JSON.stringify({ round_number: 1, active: [], completed: [] }),
|
|
98
|
+
}),
|
|
99
|
+
)
|
|
100
|
+
await page.route('**/api/diff/**', (route) =>
|
|
101
|
+
route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify({ diff: '', source: 'compare' }) }),
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const detail = (page: Page) => page.locator('[data-region="detail"]')
|
|
106
|
+
const list = (page: Page) => page.locator('[data-region="list"]')
|
|
107
|
+
|
|
108
|
+
test.describe('deep-link cold load (#1551)', () => {
|
|
109
|
+
test('goto /sessions cold restores the Sessions view', async ({ page }, testInfo) => {
|
|
110
|
+
await mockApi(page)
|
|
111
|
+
await page.goto('/sessions')
|
|
112
|
+
|
|
113
|
+
await expect(page.getByRole('heading', { name: 'Sessions' })).toBeVisible()
|
|
114
|
+
|
|
115
|
+
if (testInfo.project.name === 'wide') {
|
|
116
|
+
await expect(page.locator('[data-region="rail"]').getByRole('button', { name: /^Sessions/ })).toHaveAttribute(
|
|
117
|
+
'aria-current',
|
|
118
|
+
'page',
|
|
119
|
+
)
|
|
120
|
+
}
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('goto a pipeline item URL cold restores that item\'s detail, without visiting Home first', async ({
|
|
124
|
+
page,
|
|
125
|
+
}, testInfo) => {
|
|
126
|
+
await mockApi(page)
|
|
127
|
+
await page.goto('/pipeline/api/42')
|
|
128
|
+
|
|
129
|
+
await expect(detail(page).getByText('Fix the dashboard rendering')).toBeVisible()
|
|
130
|
+
|
|
131
|
+
if (testInfo.project.name === 'wide') {
|
|
132
|
+
// Cold-loading straight into a detail route still gives wide its whole
|
|
133
|
+
// three-column layout -- the list panel didn't need a Home visit first.
|
|
134
|
+
await expect(list(page).getByText('Refactor merge queue')).toBeVisible()
|
|
135
|
+
} else {
|
|
136
|
+
// Narrow's drill-in: landing directly on a detail URL shows only the
|
|
137
|
+
// detail, and Back returns to the list -- the list was never dropped,
|
|
138
|
+
// it just wasn't the first thing rendered.
|
|
139
|
+
await expect(list(page)).toHaveCount(0)
|
|
140
|
+
await page.getByLabel('Back').click()
|
|
141
|
+
await expect(list(page).getByText('Fix the dashboard rendering')).toBeVisible()
|
|
142
|
+
await expect(detail(page)).toHaveCount(0)
|
|
143
|
+
}
|
|
144
|
+
})
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
test.describe('phone regression net (#1551, #741)', () => {
|
|
148
|
+
test('render -> filter -> open detail -> back still all work at narrow width', async ({ page }, testInfo) => {
|
|
149
|
+
test.skip(testInfo.project.name !== 'narrow', 'narrow-only — see smoke.spec.ts for the full net')
|
|
150
|
+
|
|
151
|
+
await mockApi(page)
|
|
152
|
+
await page.goto('/')
|
|
153
|
+
|
|
154
|
+
// Render: seeded cards visible.
|
|
155
|
+
await expect(page.getByText('Fix the dashboard rendering')).toBeVisible()
|
|
156
|
+
await expect(page.getByText('Refactor merge queue')).toBeVisible()
|
|
157
|
+
|
|
158
|
+
// Filter: Needs-me hides both (seeded items carry no available_gates).
|
|
159
|
+
await page.getByRole('tab', { name: /needs.me/i }).click()
|
|
160
|
+
await expect(page.getByText('Fix the dashboard rendering')).toHaveCount(0)
|
|
161
|
+
await page.getByRole('tab', { name: 'Active' }).click()
|
|
162
|
+
|
|
163
|
+
// Drill in: click opens detail at the addressable repo/issue URL (#1548).
|
|
164
|
+
await page.getByText('Fix the dashboard rendering').click()
|
|
165
|
+
await expect(page).toHaveURL(/\/pipeline\/api\/42/)
|
|
166
|
+
await expect(detail(page).getByText('Fix the dashboard rendering')).toBeVisible()
|
|
167
|
+
|
|
168
|
+
// Back: returns to the list, drill-in intact.
|
|
169
|
+
await page.getByLabel('Back').click()
|
|
170
|
+
await expect(list(page).getByText('Fix the dashboard rendering')).toBeVisible()
|
|
171
|
+
})
|
|
172
|
+
})
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spawns a REAL `coord web --fixture` process (#1538, coord/dashboard/fixture.py)
|
|
3
|
+
* for `live-update-fixture.spec.ts` (#1551, M-W1's exit gate).
|
|
4
|
+
*
|
|
5
|
+
* Every other spec in this repo's Playwright suites — `e2e/`'s own
|
|
6
|
+
* `realtime.spec.ts` included — intercepts its own API calls with
|
|
7
|
+
* `page.route()`, or fakes `window.EventSource` outright, against the Vite
|
|
8
|
+
* dev server (see `playwright.config.ts` / `playwright.acceptance.config.ts`'s
|
|
9
|
+
* documented contracts). That's deliberate and fast, but it can only prove
|
|
10
|
+
* the *app* reacts correctly to a given wire shape — never that the real
|
|
11
|
+
* server (`coord/dashboard/fixture.py`'s `FixtureServer` + its
|
|
12
|
+
* `/api/fixture/events/replay` trigger) actually emits that shape. #1551 asks
|
|
13
|
+
* for exactly that: "a scripted SSE sequence from the fixture server". This
|
|
14
|
+
* module is what lets `live-update-fixture.spec.ts` be the one spec in the
|
|
15
|
+
* repo that talks to a real `coord web` process instead of a mock.
|
|
16
|
+
*
|
|
17
|
+
* `--dist` needs a production build, so `startFixtureServer` runs one itself
|
|
18
|
+
* rather than trusting an out-of-band `npm run build` to be fresh — worth the
|
|
19
|
+
* ~5s given what a stale-`dist/` false pass would cost.
|
|
20
|
+
*
|
|
21
|
+
* Not part of `tests/acceptance/ms-52/` (the milestone's *sealed* slice,
|
|
22
|
+
* `tests/acceptance/**`) — a Work-type session cannot write there by design
|
|
23
|
+
* (see this file's spec's header comment). This is the worker-authored
|
|
24
|
+
* black-box coverage the story falls back to per its own "Notes" section
|
|
25
|
+
* when dispatched after M-W0 without a pre-authored sealed slice.
|
|
26
|
+
*/
|
|
27
|
+
import { type ChildProcessWithoutNullStreams, spawn, spawnSync } from 'node:child_process'
|
|
28
|
+
import { createServer } from 'node:net'
|
|
29
|
+
import path from 'node:path'
|
|
30
|
+
import { fileURLToPath } from 'node:url'
|
|
31
|
+
|
|
32
|
+
const here = path.dirname(fileURLToPath(import.meta.url))
|
|
33
|
+
export const WEBAPP_ROOT = path.resolve(here, '..')
|
|
34
|
+
export const REPO_ROOT = path.resolve(here, '../../../..')
|
|
35
|
+
export const DIST_DIR = path.join(WEBAPP_ROOT, 'dist')
|
|
36
|
+
export const FIXTURE_PATH = path.join(REPO_ROOT, 'tests/fixtures/board-pipeline-basic.json')
|
|
37
|
+
|
|
38
|
+
export interface FixtureServerHandle {
|
|
39
|
+
baseUrl: string
|
|
40
|
+
proc: ChildProcessWithoutNullStreams
|
|
41
|
+
/** Graceful SIGTERM, escalating to SIGKILL after 3s if it won't die. */
|
|
42
|
+
stop: () => Promise<void>
|
|
43
|
+
/** Hard kill, for the "a dropped stream" test itself. */
|
|
44
|
+
kill: () => void
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** An OS-assigned free TCP port — avoids clashing with the Vite dev server
|
|
48
|
+
* (5173) or a concurrent acceptance run on the same machine. */
|
|
49
|
+
async function freePort(): Promise<number> {
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const srv = createServer()
|
|
52
|
+
srv.once('error', reject)
|
|
53
|
+
srv.listen(0, '127.0.0.1', () => {
|
|
54
|
+
const address = srv.address()
|
|
55
|
+
if (address === null || typeof address === 'string') {
|
|
56
|
+
reject(new Error('could not determine a free port'))
|
|
57
|
+
return
|
|
58
|
+
}
|
|
59
|
+
const { port } = address
|
|
60
|
+
srv.close(() => resolve(port))
|
|
61
|
+
})
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** Builds the production bundle `coord web --dist` serves. Synchronous and
|
|
66
|
+
* blocking on purpose — nothing here should start before it's on disk. */
|
|
67
|
+
function buildDist(): void {
|
|
68
|
+
const result = spawnSync('npm', ['run', 'build'], {
|
|
69
|
+
cwd: WEBAPP_ROOT,
|
|
70
|
+
stdio: 'pipe',
|
|
71
|
+
encoding: 'utf-8',
|
|
72
|
+
})
|
|
73
|
+
if (result.status !== 0) {
|
|
74
|
+
throw new Error(
|
|
75
|
+
`npm run build failed (exit ${String(result.status)}) — live-update-fixture.spec.ts needs ` +
|
|
76
|
+
`a fresh dist/ to serve via 'coord web --dist':\n${result.stdout}\n${result.stderr}`,
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
async function waitForReady(baseUrl: string, timeoutMs = 15_000): Promise<void> {
|
|
82
|
+
const deadline = Date.now() + timeoutMs
|
|
83
|
+
let lastError: unknown
|
|
84
|
+
while (Date.now() < deadline) {
|
|
85
|
+
try {
|
|
86
|
+
const res = await fetch(`${baseUrl}/api/board`)
|
|
87
|
+
if (res.ok) return
|
|
88
|
+
} catch (err) {
|
|
89
|
+
lastError = err
|
|
90
|
+
}
|
|
91
|
+
await new Promise((r) => setTimeout(r, 150))
|
|
92
|
+
}
|
|
93
|
+
throw new Error(`coord web --fixture never became ready at ${baseUrl}: ${String(lastError)}`)
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Starts a fresh `coord web --fixture <fixturePath>` on a free port, serving
|
|
98
|
+
* the just-built `dist/`. Resolves `coord` on `$PATH` exactly the way a real
|
|
99
|
+
* operator's shell (or the acceptance driver's `run:` command) would —
|
|
100
|
+
* deliberately not hard-coded to any one machine's venv path.
|
|
101
|
+
*
|
|
102
|
+
* `fixturePath` defaults to `FIXTURE_PATH` (`board-pipeline-basic.json`, the
|
|
103
|
+
* #1538 reference board `live-update-fixture.spec.ts` uses) — pass a
|
|
104
|
+
* different fixture to drive the real server against other seeded scenarios
|
|
105
|
+
* (e.g. `available-gates-terminal.spec.ts`'s #2084 fixture) without
|
|
106
|
+
* duplicating the subprocess-management plumbing.
|
|
107
|
+
*/
|
|
108
|
+
export async function startFixtureServer(fixturePath: string = FIXTURE_PATH): Promise<FixtureServerHandle> {
|
|
109
|
+
buildDist()
|
|
110
|
+
const port = await freePort()
|
|
111
|
+
const baseUrl = `http://127.0.0.1:${port}`
|
|
112
|
+
|
|
113
|
+
const proc = spawn(
|
|
114
|
+
'coord',
|
|
115
|
+
['web', '--fixture', fixturePath, '--dist', DIST_DIR, '--host', '127.0.0.1', '--port', String(port)],
|
|
116
|
+
{ cwd: REPO_ROOT, stdio: 'pipe' },
|
|
117
|
+
)
|
|
118
|
+
let output = ''
|
|
119
|
+
proc.stdout.on('data', (chunk) => (output += String(chunk)))
|
|
120
|
+
proc.stderr.on('data', (chunk) => (output += String(chunk)))
|
|
121
|
+
|
|
122
|
+
const exited = new Promise<number | null>((resolve) => {
|
|
123
|
+
proc.once('exit', (code) => resolve(code))
|
|
124
|
+
})
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
await Promise.race([
|
|
128
|
+
waitForReady(baseUrl),
|
|
129
|
+
exited.then((code) => {
|
|
130
|
+
throw new Error(`coord web exited early (code ${String(code)}) before it became ready:\n${output}`)
|
|
131
|
+
}),
|
|
132
|
+
])
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (!proc.killed) proc.kill('SIGKILL')
|
|
135
|
+
throw err
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
baseUrl,
|
|
140
|
+
proc,
|
|
141
|
+
kill: () => proc.kill('SIGKILL'),
|
|
142
|
+
stop: () =>
|
|
143
|
+
new Promise<void>((resolve) => {
|
|
144
|
+
if (proc.exitCode !== null || proc.signalCode !== null) {
|
|
145
|
+
resolve()
|
|
146
|
+
return
|
|
147
|
+
}
|
|
148
|
+
proc.once('exit', () => resolve())
|
|
149
|
+
proc.kill('SIGTERM')
|
|
150
|
+
setTimeout(() => {
|
|
151
|
+
if (proc.exitCode === null && proc.signalCode === null) proc.kill('SIGKILL')
|
|
152
|
+
}, 3_000)
|
|
153
|
+
}),
|
|
154
|
+
}
|
|
155
|
+
}
|