secondshift 0.1.0b12__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.
- secondshift/__init__.py +1 -0
- secondshift/__main__.py +3 -0
- secondshift/cli.py +17 -0
- secondshift/config/__init__.py +0 -0
- secondshift/config/api.py +23 -0
- secondshift/config/app.py +62 -0
- secondshift/config/constants.py +279 -0
- secondshift/config/container_env.py +118 -0
- secondshift/config/first_run.py +40 -0
- secondshift/config/formatting.py +5 -0
- secondshift/config/gunicorn.py +31 -0
- secondshift/config/logging.py +40 -0
- secondshift/config/settings/__init__.py +0 -0
- secondshift/config/settings/base.py +524 -0
- secondshift/config/settings/dev.py +14 -0
- secondshift/config/settings/local.py +23 -0
- secondshift/config/settings/production.py +113 -0
- secondshift/config/settings/runner.py +140 -0
- secondshift/config/settings/test.py +47 -0
- secondshift/config/static_storage.py +45 -0
- secondshift/config/urls.py +42 -0
- secondshift/connections/__init__.py +0 -0
- secondshift/connections/access.py +27 -0
- secondshift/connections/admin.py +25 -0
- secondshift/connections/agents.py +224 -0
- secondshift/connections/api.py +747 -0
- secondshift/connections/apps.py +6 -0
- secondshift/connections/catalog.py +96 -0
- secondshift/connections/checks.py +22 -0
- secondshift/connections/dao.py +305 -0
- secondshift/connections/errors.py +13 -0
- secondshift/connections/execution.py +262 -0
- secondshift/connections/external.py +258 -0
- secondshift/connections/github.py +123 -0
- secondshift/connections/http_api/__init__.py +1 -0
- secondshift/connections/http_api/client.py +161 -0
- secondshift/connections/http_api/connections.py +187 -0
- secondshift/connections/http_api/spec.py +382 -0
- secondshift/connections/mcp/__init__.py +1 -0
- secondshift/connections/mcp/base.py +139 -0
- secondshift/connections/mcp/client.py +353 -0
- secondshift/connections/mcp/custom.py +86 -0
- secondshift/connections/mcp/github.py +256 -0
- secondshift/connections/mcp/oauth.py +352 -0
- secondshift/connections/mcp/providers.py +15 -0
- secondshift/connections/mcp/schemas.py +77 -0
- secondshift/connections/mcp/sentry.py +135 -0
- secondshift/connections/mcp/signin.py +206 -0
- secondshift/connections/migration_helpers.py +28 -0
- secondshift/connections/migrations/0001_initial.py +101 -0
- secondshift/connections/migrations/0002_alter_connection_kind.py +18 -0
- secondshift/connections/migrations/0003_require_approval.py +23 -0
- secondshift/connections/migrations/0004_new_connections_skip_approval.py +18 -0
- secondshift/connections/migrations/0005_agentconnection.py +36 -0
- secondshift/connections/migrations/0006_remove_is_default.py +23 -0
- secondshift/connections/migrations/0007_agent_last_check.py +18 -0
- secondshift/connections/migrations/0008_agentcredentialcleanup.py +25 -0
- secondshift/connections/migrations/0009_release_0_1_0b11.py +52 -0
- secondshift/connections/migrations/0010_connection_attribution.py +18 -0
- secondshift/connections/migrations/__init__.py +0 -0
- secondshift/connections/model_providers.py +441 -0
- secondshift/connections/models.py +246 -0
- secondshift/connections/outbound.py +317 -0
- secondshift/connections/repository_ref.py +19 -0
- secondshift/connections/schemas.py +313 -0
- secondshift/connections/serialization.py +100 -0
- secondshift/connections/services.py +94 -0
- secondshift/connections/tools.py +38 -0
- secondshift/connections/webhooks.py +107 -0
- secondshift/core/__init__.py +0 -0
- secondshift/core/adapters.py +38 -0
- secondshift/core/admin.py +5 -0
- secondshift/core/api.py +104 -0
- secondshift/core/apps.py +20 -0
- secondshift/core/launcher.py +311 -0
- secondshift/core/local_agents.py +90 -0
- secondshift/core/local_output.py +120 -0
- secondshift/core/management/__init__.py +0 -0
- secondshift/core/management/commands/__init__.py +0 -0
- secondshift/core/management/commands/bootstrap_dev.py +33 -0
- secondshift/core/management/commands/dev_reset_db.py +204 -0
- secondshift/core/management/commands/login.py +23 -0
- secondshift/core/management/commands/logs.py +58 -0
- secondshift/core/management/commands/status.py +46 -0
- secondshift/core/management/commands/up.py +244 -0
- secondshift/core/middleware.py +21 -0
- secondshift/core/migrations/__init__.py +0 -0
- secondshift/core/schemas.py +71 -0
- secondshift/core/signin.py +58 -0
- secondshift/core/static/core/account.css +149 -0
- secondshift/core/static/core/admin.css +135 -0
- secondshift/core/templatetags/__init__.py +0 -0
- secondshift/core/templatetags/accounts.py +20 -0
- secondshift/core/terminal.py +27 -0
- secondshift/core/views.py +10 -0
- secondshift/gateway/__init__.py +0 -0
- secondshift/gateway/apps.py +6 -0
- secondshift/gateway/dao.py +137 -0
- secondshift/gateway/metering.py +171 -0
- secondshift/gateway/migrations/0001_initial.py +54 -0
- secondshift/gateway/migrations/0002_modelusage_gateway_usage_created_at_idx.py +19 -0
- secondshift/gateway/migrations/__init__.py +0 -0
- secondshift/gateway/models.py +61 -0
- secondshift/gateway/pricing.py +39 -0
- secondshift/gateway/proxy.py +369 -0
- secondshift/gateway/tokens.py +68 -0
- secondshift/gateway/urls.py +17 -0
- secondshift/gateway/views.py +11 -0
- secondshift/github/__init__.py +6 -0
- secondshift/github/app.py +109 -0
- secondshift/github/authors.py +56 -0
- secondshift/github/credentials.py +59 -0
- secondshift/github/errors.py +2 -0
- secondshift/github/git.py +56 -0
- secondshift/github/identity.py +35 -0
- secondshift/github/installations.py +74 -0
- secondshift/github/issues.py +75 -0
- secondshift/github/permissions.py +49 -0
- secondshift/github/pulls.py +49 -0
- secondshift/github/repos.py +52 -0
- secondshift/github/rest.py +93 -0
- secondshift/github/ruff.toml +16 -0
- secondshift/github/tokens.py +52 -0
- secondshift/jobs/__init__.py +0 -0
- secondshift/jobs/access.py +12 -0
- secondshift/jobs/admin.py +83 -0
- secondshift/jobs/api.py +184 -0
- secondshift/jobs/apps.py +6 -0
- secondshift/jobs/automatic_grants.py +115 -0
- secondshift/jobs/autosync.py +78 -0
- secondshift/jobs/capabilities.py +43 -0
- secondshift/jobs/dao.py +258 -0
- secondshift/jobs/drafts.py +112 -0
- secondshift/jobs/grants.py +116 -0
- secondshift/jobs/limits.py +26 -0
- secondshift/jobs/management/__init__.py +0 -0
- secondshift/jobs/management/commands/__init__.py +0 -0
- secondshift/jobs/manifest.py +364 -0
- secondshift/jobs/migrations/0001_initial.py +87 -0
- secondshift/jobs/migrations/0002_job_history.py +18 -0
- secondshift/jobs/migrations/0003_job_skill_text.py +23 -0
- secondshift/jobs/migrations/0004_jobgrant_tool_rules.py +18 -0
- secondshift/jobs/migrations/0005_job_triggers.py +18 -0
- secondshift/jobs/migrations/0006_job_webhook.py +18 -0
- secondshift/jobs/migrations/0007_job_deliveries.py +23 -0
- secondshift/jobs/migrations/0008_job_removed_at.py +18 -0
- secondshift/jobs/migrations/0009_job_subagents.py +18 -0
- secondshift/jobs/migrations/0010_job_skill_source.py +18 -0
- secondshift/jobs/migrations/__init__.py +0 -0
- secondshift/jobs/models.py +199 -0
- secondshift/jobs/needs.py +154 -0
- secondshift/jobs/plan.py +176 -0
- secondshift/jobs/planning.py +386 -0
- secondshift/jobs/readiness.py +46 -0
- secondshift/jobs/repository_sync.py +199 -0
- secondshift/jobs/scheduling.py +26 -0
- secondshift/jobs/schemas.py +207 -0
- secondshift/jobs/serialization.py +218 -0
- secondshift/jobs/skill_needs.py +189 -0
- secondshift/jobs/sync.py +252 -0
- secondshift/jobs/tool_policy.py +82 -0
- secondshift/jobs/tool_rules.py +200 -0
- secondshift/mcp_server/__init__.py +6 -0
- secondshift/mcp_server/api.py +79 -0
- secondshift/mcp_server/apps.py +7 -0
- secondshift/mcp_server/audit.py +22 -0
- secondshift/mcp_server/core.py +12 -0
- secondshift/mcp_server/dao.py +23 -0
- secondshift/mcp_server/migrations/0001_initial.py +33 -0
- secondshift/mcp_server/migrations/__init__.py +0 -0
- secondshift/mcp_server/models.py +40 -0
- secondshift/mcp_server/schemas.py +204 -0
- secondshift/mcp_server/tools/__init__.py +9 -0
- secondshift/mcp_server/tools/jobs.py +106 -0
- secondshift/mcp_server/tools/manifests.py +127 -0
- secondshift/mcp_server/tools/repositories.py +42 -0
- secondshift/mcp_server/tools/runs.py +86 -0
- secondshift/mcp_server/tools/tasks.py +103 -0
- secondshift/mcp_server/tools_registry.py +81 -0
- secondshift/repositories/__init__.py +0 -0
- secondshift/repositories/access.py +13 -0
- secondshift/repositories/admin.py +19 -0
- secondshift/repositories/api.py +296 -0
- secondshift/repositories/apps.py +6 -0
- secondshift/repositories/dao.py +114 -0
- secondshift/repositories/external_skills.py +224 -0
- secondshift/repositories/github.py +27 -0
- secondshift/repositories/github_access.py +57 -0
- secondshift/repositories/github_check.py +279 -0
- secondshift/repositories/github_sync.py +181 -0
- secondshift/repositories/github_views.py +90 -0
- secondshift/repositories/local.py +198 -0
- secondshift/repositories/management/__init__.py +0 -0
- secondshift/repositories/management/commands/__init__.py +0 -0
- secondshift/repositories/management/commands/prune_source_cache.py +15 -0
- secondshift/repositories/migrations/0001_initial.py +45 -0
- secondshift/repositories/migrations/0002_repository_last_sync_error.py +18 -0
- secondshift/repositories/migrations/0003_repository_checked_sha.py +18 -0
- secondshift/repositories/migrations/0004_pull_request_settings.py +23 -0
- secondshift/repositories/migrations/0005_remove_repository_installation_id.py +17 -0
- secondshift/repositories/migrations/0006_allow_issues.py +18 -0
- secondshift/repositories/migrations/__init__.py +0 -0
- secondshift/repositories/mirrors.py +50 -0
- secondshift/repositories/models.py +104 -0
- secondshift/repositories/pull_requests.py +199 -0
- secondshift/repositories/schemas.py +102 -0
- secondshift/repositories/serialization.py +133 -0
- secondshift/repositories/skill_source.py +64 -0
- secondshift/repositories/snapshots.py +238 -0
- secondshift/repositories/sources.py +256 -0
- secondshift/repositories/status.py +67 -0
- secondshift/runner/README.md +65 -0
- secondshift/runner/__init__.py +1 -0
- secondshift/runner/agent_credentials.py +89 -0
- secondshift/runner/agent_image/Dockerfile +16 -0
- secondshift/runner/agent_image/agent.py +132 -0
- secondshift/runner/agent_image/claude.py +254 -0
- secondshift/runner/agent_image/codex.py +84 -0
- secondshift/runner/agent_image/outcomes.py +120 -0
- secondshift/runner/agent_image/subagent_hook.py +71 -0
- secondshift/runner/agent_protocol.py +41 -0
- secondshift/runner/agents.py +254 -0
- secondshift/runner/api.py +123 -0
- secondshift/runner/apps.py +6 -0
- secondshift/runner/client.py +168 -0
- secondshift/runner/collection.py +276 -0
- secondshift/runner/dao.py +64 -0
- secondshift/runner/dev.py +19 -0
- secondshift/runner/images.py +57 -0
- secondshift/runner/migrations/0001_initial.py +48 -0
- secondshift/runner/migrations/0002_session_source_sha256.py +18 -0
- secondshift/runner/migrations/0003_session_collect_changes.py +18 -0
- secondshift/runner/migrations/0004_session_history_sha256.py +18 -0
- secondshift/runner/migrations/0005_require_source_sha256.py +18 -0
- secondshift/runner/migrations/0006_session_skill_sha256.py +18 -0
- secondshift/runner/migrations/__init__.py +0 -0
- secondshift/runner/models.py +53 -0
- secondshift/runner/protocol.py +97 -0
- secondshift/runner/schemas.py +36 -0
- secondshift/runner/services.py +465 -0
- secondshift/runner/sources.py +130 -0
- secondshift/runner/urls.py +7 -0
- secondshift/runs/__init__.py +0 -0
- secondshift/runs/access.py +13 -0
- secondshift/runs/admin.py +89 -0
- secondshift/runs/api.py +211 -0
- secondshift/runs/apps.py +11 -0
- secondshift/runs/artifacts.py +261 -0
- secondshift/runs/dao.py +640 -0
- secondshift/runs/deliveries.py +80 -0
- secondshift/runs/execution/__init__.py +0 -0
- secondshift/runs/execution/access_needs.py +89 -0
- secondshift/runs/execution/agent.py +91 -0
- secondshift/runs/execution/agent_broker.py +198 -0
- secondshift/runs/execution/budgets.py +177 -0
- secondshift/runs/execution/cli_agent.py +167 -0
- secondshift/runs/execution/consent.py +58 -0
- secondshift/runs/execution/deadlines.py +47 -0
- secondshift/runs/execution/external_tools.py +84 -0
- secondshift/runs/execution/issues.py +99 -0
- secondshift/runs/execution/model_gateway.py +90 -0
- secondshift/runs/execution/payload.py +63 -0
- secondshift/runs/execution/result.py +59 -0
- secondshift/runs/execution/run.py +345 -0
- secondshift/runs/execution/subagents.py +246 -0
- secondshift/runs/execution/tool_broker.py +123 -0
- secondshift/runs/execution/workspace_tools.py +248 -0
- secondshift/runs/history.py +182 -0
- secondshift/runs/inputs.py +49 -0
- secondshift/runs/issue_delivery.py +144 -0
- secondshift/runs/management/__init__.py +0 -0
- secondshift/runs/management/commands/__init__.py +0 -0
- secondshift/runs/management/commands/prune_runs.py +12 -0
- secondshift/runs/management/commands/run_dbos_worker.py +129 -0
- secondshift/runs/migration_helpers.py +313 -0
- secondshift/runs/migrations/0001_initial.py +69 -0
- secondshift/runs/migrations/0002_run_started_by.py +18 -0
- secondshift/runs/migrations/0003_agentlease.py +25 -0
- secondshift/runs/migrations/0004_task_assignment.py +39 -0
- secondshift/runs/migrations/0005_backfill_tasks.py +19 -0
- secondshift/runs/migrations/0006_standalone_tasks.py +50 -0
- secondshift/runs/migrations/0007_run_delivery.py +35 -0
- secondshift/runs/migrations/0008_run_outputs.py +18 -0
- secondshift/runs/migrations/0009_pull_request_delivery.py +28 -0
- secondshift/runs/migrations/0010_run_issue.py +32 -0
- secondshift/runs/migrations/0011_issues_delivery.py +23 -0
- secondshift/runs/migrations/0012_alter_rundelivery_status.py +18 -0
- secondshift/runs/migrations/0013_release_0_1_0b11.py +113 -0
- secondshift/runs/migrations/0014_run_access_need.py +30 -0
- secondshift/runs/migrations/__init__.py +0 -0
- secondshift/runs/models.py +340 -0
- secondshift/runs/orchestration/__init__.py +0 -0
- secondshift/runs/orchestration/admission.py +15 -0
- secondshift/runs/orchestration/dbos_runtime.py +43 -0
- secondshift/runs/orchestration/dispatch.py +130 -0
- secondshift/runs/orchestration/periodic.py +39 -0
- secondshift/runs/orchestration/reconciliation.py +98 -0
- secondshift/runs/orchestration/schedules.py +51 -0
- secondshift/runs/orchestration/workflows.py +480 -0
- secondshift/runs/outputs.py +52 -0
- secondshift/runs/publishing.py +162 -0
- secondshift/runs/queue.py +26 -0
- secondshift/runs/reporting/__init__.py +0 -0
- secondshift/runs/reporting/collection.py +26 -0
- secondshift/runs/reporting/connection_usage.py +45 -0
- secondshift/runs/reporting/failures.py +150 -0
- secondshift/runs/reporting/health.py +158 -0
- secondshift/runs/reporting/notifications.py +258 -0
- secondshift/runs/reporting/queries.py +60 -0
- secondshift/runs/retention.py +35 -0
- secondshift/runs/schemas.py +192 -0
- secondshift/runs/serialization.py +245 -0
- secondshift/runs/services.py +176 -0
- secondshift/runs/signals.py +25 -0
- secondshift/runs/submissions.py +236 -0
- secondshift/runs/transitions.py +84 -0
- secondshift/templates/account/password_reset_help_text.html +1 -0
- secondshift/templates/admin/base_site.html +24 -0
- secondshift/templates/allauth/elements/provider.html +9 -0
- secondshift/templates/allauth/layouts/base.html +67 -0
- secondshift/templates/socialaccount/snippets/login.html +9 -0
- secondshift/templates/workspaces/invite_unusable.html +10 -0
- secondshift/triggers/__init__.py +0 -0
- secondshift/triggers/admin.py +19 -0
- secondshift/triggers/api.py +116 -0
- secondshift/triggers/apps.py +6 -0
- secondshift/triggers/dao.py +66 -0
- secondshift/triggers/filters.py +48 -0
- secondshift/triggers/migrations/0001_initial.py +55 -0
- secondshift/triggers/migrations/0002_ignored_events_and_hourly_limit.py +23 -0
- secondshift/triggers/migrations/__init__.py +0 -0
- secondshift/triggers/models.py +83 -0
- secondshift/triggers/schemas.py +58 -0
- secondshift/triggers/serialization.py +91 -0
- secondshift/triggers/services.py +246 -0
- secondshift/triggers/verification.py +84 -0
- secondshift/web/assets/index-Bwy0hvk_.js +2419 -0
- secondshift/web/assets/index-D27gB9_L.css +1 -0
- secondshift/web/favicon.svg +19 -0
- secondshift/web/index.html +16 -0
- secondshift/web/secondshift-mark.svg +16 -0
- secondshift/workspaces/__init__.py +0 -0
- secondshift/workspaces/access.py +181 -0
- secondshift/workspaces/admin.py +37 -0
- secondshift/workspaces/api.py +264 -0
- secondshift/workspaces/apps.py +6 -0
- secondshift/workspaces/context_processors.py +11 -0
- secondshift/workspaces/dao.py +281 -0
- secondshift/workspaces/invites.py +150 -0
- secondshift/workspaces/limits.py +197 -0
- secondshift/workspaces/management/__init__.py +0 -0
- secondshift/workspaces/management/commands/__init__.py +0 -0
- secondshift/workspaces/management/commands/create_workspace.py +39 -0
- secondshift/workspaces/migrations/0001_initial.py +67 -0
- secondshift/workspaces/migrations/0002_api_token.py +34 -0
- secondshift/workspaces/migrations/0003_execution_default.py +29 -0
- secondshift/workspaces/migrations/0004_copy_default_model_key.py +26 -0
- secondshift/workspaces/migrations/__init__.py +0 -0
- secondshift/workspaces/models.py +169 -0
- secondshift/workspaces/owners.py +58 -0
- secondshift/workspaces/schemas.py +149 -0
- secondshift/workspaces/serialization.py +138 -0
- secondshift/workspaces/server_settings.py +140 -0
- secondshift/workspaces/services.py +94 -0
- secondshift/workspaces/tokens.py +108 -0
- secondshift/workspaces/views.py +32 -0
- secondshift-0.1.0b12.dist-info/METADATA +270 -0
- secondshift-0.1.0b12.dist-info/RECORD +371 -0
- secondshift-0.1.0b12.dist-info/WHEEL +4 -0
- secondshift-0.1.0b12.dist-info/entry_points.txt +2 -0
- secondshift-0.1.0b12.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
@import "https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;500&family=IBM+Plex+Sans:wght@400;500;600&display=swap";:root{--font-sans:"IBM Plex Sans", system-ui, sans-serif;--font-mono:"IBM Plex Mono", ui-monospace, monospace;font-family:var(--font-sans);color:#ededed;font-synthesis:none;--night-950:#0d0d0d;--night-900:#131313;--night-850:#191919;--night-800:#1f1f1f;--night-750:#262626;--ink:#ededed;--muted:#a3a3a3;--subtle:#737373;--focus:#a3a3a3;--card:#171717;--card-border:#232323;--raised:#242424;--raised-hover:#2a2a2a;--raised-border:#2e2e2e;--raised-shadow:0 1px 2px #0006;--text-xs:12px;--text-sm:13px;--text-md:14px;--text-lg:16px;--text-xl:22px;--text-2xl:28px;--radius-sm:6px;--radius-md:8px;--radius-lg:12px;--field:#181818;--field-border:#262626;--divider:#1c1c1c;--amber:#ffb454;--mint:#71dbb6;--danger:#ff778b;--white-3:#ffffff08;--white-6:#ffffff0f;--white-8:#ffffff14;--white-12:#ffffff1f;--white-18:#ffffff2e;--border:var(--white-8);--shadow:none;background:#131313}*{box-sizing:border-box}body{background:var(--night-900);min-width:320px;min-height:100vh;margin:0}code,pre,kbd{font-family:var(--font-mono)}button,input{font:inherit}a{color:inherit;text-decoration:none}h1,h2,h3,p{margin-top:0}h1,h2,h3{font-weight:600}.boot-screen{min-height:100vh;color:var(--muted);place-content:center;justify-items:center;gap:1rem;display:grid}.boot-mark{width:64px;animation:1.6s ease-in-out infinite breathe}@keyframes breathe{50%{transform:translateY(-4px)}}.app-shell{grid-template-columns:248px minmax(0,1fr);min-height:100vh;display:grid}.sidebar{--row-inset:11px;--marker:20px;background:var(--night-950);border-right:1px solid var(--divider);z-index:20;flex-direction:column;height:100vh;padding:0 12px 12px;display:flex;position:sticky;top:0}.workspace-switcher{width:100%;height:40px;padding:0 var(--row-inset);border-radius:var(--radius-md);color:var(--ink);font:inherit;text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:10px;margin:12px 0;transition:all .18s;display:flex}.workspace-switcher:hover,.workspace-switcher.is-open{border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow)}.workspace-switcher:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.workspace-switcher.static:hover{box-shadow:none;background:0 0;border-color:#0000}.workspace-switcher>.app-icon{color:var(--subtle);margin-left:auto;font-size:16px}.brand-mark{flex:none;width:22px;height:22px}.workspace-name{min-width:0;font-size:var(--text-md);letter-spacing:-.01em;text-overflow:ellipsis;white-space:nowrap;font-weight:600;overflow:hidden}.create-workspace{gap:8px;width:100%;display:grid}.create-workspace-row{flex-wrap:wrap;gap:8px;display:flex}.create-workspace-row .p-inputtext{flex:220px}.dialog-lead{color:var(--muted);margin:0 0 16px;line-height:1.6}.sidebar nav{margin:0 -12px;display:grid}.nav-group{border-top:1px solid var(--divider);gap:2px;padding:12px;display:grid}.nav-label{color:var(--subtle);font-size:var(--text-xs);letter-spacing:0;margin:0 12px 8px;font-weight:400}.sidebar nav a{padding:6px var(--row-inset);border-radius:var(--radius-md);color:#c4c4c4;font-weight:500;font-size:var(--text-md);border:1px solid #0000;align-items:center;gap:10px;transition:all .18s;display:flex}.sidebar nav a .app-icon{width:var(--marker);height:var(--marker)}.sidebar nav a>span{flex:1}.sidebar nav a:hover{background:var(--white-6);color:var(--ink)}.sidebar nav a.active{border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink)}.nav-note{color:var(--muted);font-size:var(--text-xs);white-space:nowrap;align-items:center;gap:6px;font-weight:500;display:inline-flex}.nav-note:before{content:"";background:var(--subtle);border-radius:50%;width:6px;height:6px}.nav-note.warning:before{background:var(--amber)}.nav-note.danger:before{background:var(--danger)}.sidebar-foot{border-top:1px solid var(--divider);gap:2px;margin:auto -12px 0;padding:12px 12px 0;display:grid}.avatar{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--white-6);width:30px;height:30px;color:var(--muted);flex:none;place-items:center;font-size:11px;font-weight:600;display:grid}.account{width:100%;height:36px;padding:0 var(--row-inset);border-radius:var(--radius-md);color:var(--muted);font-size:var(--text-md);text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:10px;font-weight:500;display:flex}.account:hover,.account.is-open{border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink)}.account:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.account-name{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.account>.app-icon{color:var(--subtle);font-size:16px}.menu-heading,.account-menu-heading{border-bottom:1px solid var(--card-border);color:var(--subtle);font-size:var(--text-xs);flex-direction:column;gap:2px;margin-bottom:4px;padding:8px 10px 10px;display:flex}.account-menu-heading strong{color:var(--ink);font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;font-weight:500;overflow:hidden}body .account-menu{width:224px}.account>.avatar{width:var(--marker);height:var(--marker);background:var(--night-750);color:var(--ink);border:0;border-radius:50%;font-size:10px}.mobile-bar,.mobile-backdrop{display:none}.app-shell>main{min-width:0}.page{width:min(1440px,100%);margin:0 auto;padding:32px 32px 48px}.page-header{justify-content:space-between;align-items:flex-end;gap:24px;margin-bottom:24px;display:flex}.page-header.compact{align-items:center;margin-bottom:20px}.page-header.compact h1{font-size:var(--text-xl);letter-spacing:-.02em;margin-bottom:4px;font-weight:500}.page-header.compact p{font-size:var(--text-sm)}.crumbs{color:var(--subtle);font-size:var(--text-sm);align-items:center;gap:8px;margin-bottom:8px;display:flex}.crumbs a{color:var(--muted)}.crumbs a:hover{color:var(--ink)}.page-header h1{font-size:var(--text-xl);letter-spacing:-.03em;margin:0 0 8px;font-weight:400;line-height:1.1}.page-header p{max-width:650px;color:var(--muted);font-size:var(--text-md);margin-bottom:0}.page-actions{gap:8px;display:flex}.eyebrow{color:var(--subtle);font-size:var(--text-xs)}.primary-action,.secondary-action{border-radius:var(--radius-md);min-height:40px;font-weight:600;font-size:var(--text-sm);justify-content:center;align-items:center;gap:8px;padding:0 16px;display:inline-flex}.primary-action{border:1px solid var(--ink);background:var(--ink);color:#0d0d0d}.primary-action:hover{background:#fff;border-color:#fff}.secondary-action,.raised{border:1px solid var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink)}.secondary-action:hover,.raised:hover{background:var(--raised-hover)}.stats-grid{border:1px solid var(--card-border);border-radius:var(--radius-lg);background:var(--card-border);grid-template-columns:repeat(4,1fr);gap:1px;margin-bottom:16px;display:grid;overflow:hidden}.stat-card{background:var(--night-900);min-height:0;padding:16px 20px;position:relative;overflow:hidden}.stat-card>span{color:var(--muted);font-size:var(--text-sm);font-weight:500;display:block}.stat-card>strong{font-size:var(--text-xl);letter-spacing:-.03em;font-variant-numeric:tabular-nums;margin:8px 0 4px;font-weight:500;display:block}.stat-card>small{color:var(--subtle);font-size:var(--text-xs)}.stat-card.danger>strong{color:var(--danger)}.stat-card>small em{color:var(--ink);margin-right:8px;font-style:normal}.stat-card>small em.good{color:var(--mint)}.title-aside{color:var(--subtle);margin-left:.3em}.stat-card>span,.stat-card>strong,.stat-card>small{color:initial}.stat-card>span{color:var(--muted)}.stat-card>strong{color:var(--ink)}.stat-card>small{color:var(--subtle)}.content-grid{grid-template-columns:minmax(0,2fr) minmax(260px,1fr);gap:16px;display:grid}.panel{border:1px solid var(--card-border);border-radius:var(--radius-lg);background:0 0}.span-two{padding:20px}.panel-heading{justify-content:space-between;align-items:center;gap:16px;margin-bottom:12px;display:flex}.panel-heading h2{font-size:var(--text-lg);letter-spacing:-.01em;margin:0;font-weight:600}.panel-heading>a{color:var(--muted);font-size:var(--text-sm);font-weight:500}.panel-heading>a:hover{color:var(--ink)}.activity-main{flex-direction:column;min-width:0;display:flex}.activity-main strong{font-size:var(--text-sm)}.activity-main span,.activity-main>a:last-child{color:var(--subtle);text-overflow:ellipsis;font-size:var(--text-xs);overflow:hidden}.activity-main a:hover{color:var(--ink)}.activity-time{color:var(--subtle);font-size:var(--text-xs);font-variant-numeric:tabular-nums}.approval-inbox{margin-bottom:16px;padding:20px}.approval-list{display:grid}.approval-row{border-top:1px solid var(--white-8);grid-template-columns:minmax(0,1fr) auto 18px;align-items:center;gap:12px;padding:12px 4px;display:grid}.approval-row>div{flex-direction:column;display:flex}.approval-row strong{font-size:var(--text-sm);font-weight:500}.approval-row span{color:var(--subtle);font-size:var(--text-xs)}.approval-row code{color:var(--muted);font-size:var(--text-xs)}.approval-row>.app-icon{color:var(--subtle)}.status-pill{--dot:var(--subtle);width:fit-content;color:var(--muted);font-size:var(--text-xs);white-space:nowrap;align-items:center;gap:8px;font-weight:500;display:inline-flex}.status-pill:before{content:"";background:var(--dot);border-radius:50%;flex:none;width:6px;height:6px}.status-pill.succeeded,.status-pill.connected,.status-pill.enabled,.status-pill.approved,.status-pill.not_required,.status-pill.valid{--dot:var(--mint)}.status-pill.failed,.status-pill.expired,.status-pill.denied,.status-pill.error{--dot:var(--danger);color:var(--ink)}.status-pill.pending,.status-pill.unconfigured,.status-pill.warning{--dot:var(--amber);color:var(--ink)}.status-pill.running,.status-pill.queued{--dot:var(--ink);color:var(--ink)}.status-pill.running:before{animation:1.6s ease-in-out infinite live-pulse}.status-pill.queued:before{opacity:.5}@keyframes live-pulse{50%{opacity:.35}}@media (prefers-reduced-motion:reduce){.status-pill.running:before{animation:none}}.empty-state{text-align:center;min-height:120px;color:var(--subtle);flex-direction:column;justify-content:center;align-items:center;padding:24px 16px;display:flex}.empty-state .app-icon{color:var(--subtle);margin-bottom:8px;font-size:20px}.empty-state strong{color:var(--ink);font-size:var(--text-md)}.empty-state span{max-width:420px;font-size:var(--text-xs);margin-top:8px}.not-found-home{margin-top:16px}.empty-state .detail-link{font-size:var(--text-xs);margin-top:12px}.empty-state.compact{width:100%;min-height:112px}.empty-state.wide{grid-column:1/-1}.card-grid{grid-template-columns:repeat(3,minmax(0,1fr));gap:16px;display:grid}.need-primary small.expiring{color:var(--amber)}.need-primary small.expired{color:var(--danger)}.github-token-form{gap:8px;display:grid}.github-token-row{gap:8px;display:flex}.github-token-row .p-inputtext{min-width:0;color:var(--ink);background:var(--field);border-color:var(--field-border);flex:1}.github-token-form>small{color:var(--subtle);font-size:var(--text-xs);line-height:1.6}.github-token-form>small a{color:var(--ink);align-items:center;gap:2px;display:inline-flex}.github-token-form .form-error{color:var(--danger);font-size:var(--text-xs);margin:0}.need-setup.github-token-form{grid-template-columns:minmax(0,1fr);gap:8px}.repository-setup{grid-template-columns:40px minmax(0,1fr) auto;align-items:center;gap:16px;margin-bottom:16px;padding:20px;display:grid}.setup-status-icon{border:1px solid var(--raised-border);border-radius:var(--radius-md);width:40px;height:40px;color:var(--muted);background:var(--raised);place-items:center;font-size:20px;display:grid}.repository-setup.complete .setup-status-icon{color:var(--mint)}.setup-copy h2{font-size:var(--text-lg);margin:4px 0}.setup-copy p{color:var(--muted);font-size:var(--text-xs);margin:0}.setup-copy small{color:var(--subtle);font-size:var(--text-xs);margin-top:8px;display:block}.repository-setup-actions{justify-content:flex-end;gap:8px;display:flex}.repository-info-card,.repository-jobs-panel{padding:20px 0 0;overflow:hidden}.repository-jobs-panel>.panel-heading,.repository-jobs-panel>.empty-state{margin-inline:20px}.repository-metadata{margin:0;display:grid}.repository-metadata>div{border-top:1px solid var(--white-8);grid-template-columns:130px minmax(0,1fr);align-items:center;gap:16px;padding:12px 0;display:grid}.repository-metadata dt{color:var(--subtle);font-size:var(--text-xs)}.repository-metadata dd{min-width:0;color:var(--muted);font-size:var(--text-xs);margin:0}.repository-metadata code{overflow-wrap:anywhere;color:var(--ink)}.copy-button{border-radius:var(--radius-sm);background:var(--white-8);width:28px;height:28px;color:var(--ink);cursor:pointer;border:0;margin-left:8px}.repository-actions{align-items:center;gap:8px;margin-bottom:16px;padding:16px 24px;display:flex}.repository-actions>div{min-width:220px;margin-right:auto}.repository-actions h2{font-size:var(--text-lg);margin:0 0 4px}.repository-actions p{color:var(--muted);font-size:var(--text-xs);margin:0}.page-error{border:1px solid color-mix(in srgb, var(--danger) 35%, transparent);border-radius:var(--radius-md);background:var(--white-3);color:var(--ink);font-size:var(--text-xs);margin:0 0 16px;padding:12px 16px}.table-panel{overflow:hidden}.runs-toolbar{flex-wrap:nowrap}.runs-toolbar .search-input{flex:auto;max-width:none}.runs-toolbar .list-select{flex:0 0 176px;min-width:0}.list-head{border-bottom:1px solid var(--divider);justify-content:space-between;align-items:center;gap:16px;padding:8px 12px;display:flex}.list-head .filter-chips{scrollbar-width:none;flex-wrap:nowrap;gap:2px;overflow-x:auto}.list-head .filter-chips button{white-space:nowrap;background:0 0;border-color:#0000;min-height:28px;padding:0 10px}.list-head .filter-chips button:hover{color:var(--ink)}.list-head .filter-chips button.active{border-color:var(--raised-border);background:var(--raised)}.list-head .sort-select{width:156px;min-width:0;font-size:var(--text-xs);background:0 0;border-color:#0000;flex:none}.list-head .sort-select .p-select-label{color:var(--muted);font-size:var(--text-xs);padding-block:4px}.data-list{display:grid;container-type:inline-size}.job-row,.run-row{border-bottom:1px solid var(--white-8);align-items:center;gap:16px;padding:16px 20px;display:grid}.job-row:last-child,.run-row:last-child{border-bottom:0}.job-primary{flex-direction:column;min-width:0;display:flex}.job-primary strong{font-size:var(--text-sm)}.job-primary span{color:var(--subtle);text-overflow:ellipsis;font-size:var(--text-xs);overflow:hidden}.job-name-link{align-items:center;gap:4px;width:fit-content;display:flex}.job-name-link .app-icon{color:var(--subtle);font-size:12px}.job-name-link:hover,.repository-inline-link:hover{color:var(--ink)}.repository-inline-link{color:var(--muted)}.run-links>a{width:fit-content}.run-links>a:last-child{color:var(--subtle);font-size:var(--text-xs);margin-top:4px}.run-links>a:hover{color:var(--ink)}.job-row>div>strong:not(.job-primary strong){font-size:var(--text-xs)}.run-id{color:var(--subtle);font-family:var(--font-mono)}.run-row>span{font-size:var(--text-xs)}.job-statuses,.run-statuses{justify-items:start;gap:4px;display:grid}.job-actions{align-items:center;display:flex}.job-review{border-bottom:1px solid var(--white-8);background:#00000047;padding:20px 24px 24px 80px}.job-review-heading{justify-content:space-between;align-items:center;gap:16px;margin-bottom:16px;display:flex}.job-review-heading h3{font-size:var(--text-md);margin:0 0 4px}.job-review-heading p{color:var(--muted);font-size:var(--text-xs);margin:0}.capability-review-row{border-top:1px solid var(--white-8);grid-template-columns:minmax(180px,.75fr) minmax(300px,1.25fr) auto auto;align-items:center;gap:16px;padding:16px 0;display:grid}.capability-summary{flex-direction:column;display:flex}.capability-summary strong{font-family:var(--font-mono);font-size:var(--text-xs)}.capability-summary span,.capability-summary small{color:var(--subtle);font-size:var(--text-xs);margin-top:4px}.connection-select.p-select{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--white-6);align-items:center;width:100%;min-height:38px}.connection-select.p-select:not(.p-disabled):hover{border-color:var(--white-12)}.connection-select .p-select-label{color:var(--ink);font-size:var(--text-xs)}.connection-select .p-select-dropdown{color:var(--subtle)}.connection-required{border:1px solid color-mix(in srgb, var(--amber) 35%, transparent);border-radius:var(--radius-md);background:var(--white-3);justify-items:start;gap:4px;padding:12px 16px;display:grid}.connection-required strong{font-size:var(--text-xs)}.connection-required code{color:var(--ink)}.connection-required small{color:var(--muted);line-height:1.45;font-size:var(--text-xs)}.step-label{color:var(--subtle);font-size:var(--text-xs)}.connection-setup-action{min-height:34px;font-size:var(--text-xs);margin-top:4px;padding:0 12px}.review-actions{gap:4px;display:flex}.job-access-panel,.job-runs-panel{padding:20px}.detail-link{color:var(--ink);font-weight:600}.job-access-panel,.job-runs-panel{margin-bottom:16px}.job-capability-details{display:grid}.job-capability-detail{border-top:1px solid var(--white-8);grid-template-columns:minmax(180px,.8fr) minmax(220px,1fr) minmax(160px,.7fr) auto;align-items:center;gap:16px;padding:16px 4px;display:grid}.job-capability-detail>div:first-child{flex-direction:column;display:flex}.job-capability-detail strong{font-family:var(--font-mono);font-size:var(--text-xs)}.job-capability-detail>div:first-child span,.capability-connection{color:var(--subtle);font-size:var(--text-xs)}.setup-actions{gap:8px;min-width:240px;display:grid}.panel-heading p{color:var(--muted);font-size:var(--text-xs);margin:4px 0 0;line-height:1.6}.needs-panel,.services-panel{margin-bottom:16px;padding:20px 20px 8px}.tool-list{flex-wrap:wrap;gap:4px;display:flex}.tool-list span{border-radius:var(--radius-sm);background:var(--white-6);color:var(--muted);font-size:var(--text-xs);padding:4px 8px}.tool-list small{color:var(--subtle)}.need-list{display:grid}.need{border-top:1px solid var(--white-8)}.need-row{grid-template-columns:40px minmax(0,1fr) auto auto;align-items:center;gap:16px;padding:12px 4px;display:grid}.need-primary{flex-direction:column;gap:4px;min-width:0;display:flex}.need-primary strong{font-family:var(--font-mono);font-size:var(--text-sm)}.need-primary span,.need-primary small{color:var(--subtle);font-size:var(--text-xs)}.need-primary small{color:var(--muted)}.need-primary .need-detail{color:var(--danger)}.need-actions{justify-content:flex-end;align-items:center;gap:8px;display:flex}.secondary-action.compact{min-height:34px;font-size:var(--text-xs);padding:0 12px}.need-notice{color:var(--danger);font-size:var(--text-xs);align-items:center;gap:8px;margin:0 4px 16px 52px;display:flex}.need-notice.ok{color:var(--mint)}.need-notice.inline{margin:2px 0 0}.need-setup{border:1px solid var(--border);border-radius:var(--radius-lg);background:#00000047;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;margin:0;padding:16px;display:grid}.need-setup.inset{margin:0 4px 16px 52px}.need-setup-fields{display:contents}.default-badge{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--muted);font-family:var(--font-sans);font-size:var(--text-xs);vertical-align:middle;margin-left:8px;padding:2px 8px;font-weight:500}.add-connection-panel{margin-bottom:16px;padding:8px}.add-connection-panel .need-setup{background:0 0;border:0}.need-setup-type,.need-setup-copy,.need-setup-check,.need-setup-footer,.need-setup-wide{grid-column:1/-1}.need-setup-type{flex-wrap:wrap;gap:8px;display:flex}.need-setup-type button{border:1px solid var(--border);border-radius:var(--radius-md);min-height:34px;color:var(--muted);background:var(--white-3);font-size:var(--text-xs);cursor:pointer;align-items:center;gap:8px;padding:0 12px;font-weight:600;display:inline-flex}.need-setup-type button.active{color:var(--ink);border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow)}.need-setup-type button:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.mcp-provider-grid{grid-column:1/-1;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px;display:grid}.mcp-provider-card{border:1px solid var(--border);border-radius:var(--radius-md);min-height:54px;color:var(--muted);background:var(--white-3);text-align:left;cursor:pointer;align-items:center;gap:12px;padding:8px 12px;display:flex}.mcp-provider-card>.app-icon{font-size:22px}.mcp-provider-card>.provider-icon{width:20px;height:20px;margin:0 1px}.provider-icon{fill:currentColor;flex:none;width:1em;height:1em}.mcp-provider-card span{gap:2px;min-width:0;display:grid}.mcp-provider-card strong{color:var(--ink);font-size:var(--text-xs)}.mcp-provider-card small{color:var(--subtle);font-size:var(--text-xs)}.mcp-provider-card:not(:disabled):hover{border-color:var(--white-12)}.mcp-provider-card.active{color:var(--ink);border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow)}.mcp-provider-card:disabled{opacity:.5;cursor:not-allowed}.mcp-provider-card:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.mcp-choice{border:1px solid var(--border);border-radius:var(--radius-md);gap:8px;margin:0;padding:12px 16px;display:grid}.mcp-choice legend{color:var(--subtle);font-size:var(--text-xs);padding:0 8px}.mcp-choice-item{color:var(--ink);font-size:var(--text-xs);cursor:pointer;align-items:center;gap:8px;text-transform:none!important;letter-spacing:0!important;flex-direction:row!important;display:flex!important}.mcp-choice-item code{font-size:var(--text-xs)}.mcp-choice small,.mcp-choice-note{color:var(--subtle);font-size:var(--text-xs);margin:0}.mcp-flag{color:var(--amber);background:var(--white-6);font-size:var(--text-xs);border-radius:999px;padding:1px 8px;font-weight:600}.mcp-flag.ok{color:var(--mint)}.link-button{color:var(--ink);font:inherit;text-decoration:underline;-webkit-text-decoration-color:var(--subtle);text-decoration-color:var(--subtle);text-underline-offset:3px;cursor:pointer;background:0 0;border:0;padding:0}.auto-sync{flex-wrap:wrap;align-items:baseline;gap:4px 8px;display:flex}.link-button:disabled{opacity:.5;cursor:default}.requested-tools{flex-wrap:wrap;gap:2px 8px;display:flex}.requested-tools>span{align-items:center;gap:4px;display:inline-flex}.api-method{border-radius:var(--radius-sm);min-width:44px;color:var(--muted);background:var(--white-6);font-family:var(--font-mono);font-size:var(--text-xs);text-align:center;padding:1px 4px;font-weight:600}.api-method.ok{color:var(--mint)}.mcp-tool-toolbar{align-items:center;gap:12px;margin-bottom:4px;display:flex}.mcp-tool-toolbar .p-inputtext{min-height:30px;font-size:var(--text-xs);flex:0 260px;padding:4px 8px}.mcp-tool-toolbar .link-button{font-size:var(--text-xs)}.mcp-tool-grid{grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:4px 16px;max-height:300px;padding:4px 2px;display:grid;overflow-y:auto}.mcp-tool-grid .mcp-choice-item code{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.need-setup label small{color:var(--subtle);text-transform:none;letter-spacing:0;font-size:var(--text-xs)}.need-setup label small.field-error{color:var(--danger)}.need-setup .p-inputtext:disabled{opacity:.65}.primary-action.disabled{opacity:.5;pointer-events:none}.need-setup-copy h3{font-size:var(--text-md);margin:0 0 4px}.need-setup-copy p{color:var(--muted);line-height:1.5;font-size:var(--text-xs);margin:0}.need-setup-copy p a{color:var(--ink);text-decoration:underline;-webkit-text-decoration-color:var(--subtle);text-decoration-color:var(--subtle);text-underline-offset:3px;white-space:nowrap}.need-setup label{color:var(--muted);font-size:var(--text-xs);gap:8px;font-weight:500;display:grid}.need-setup .p-inputtext{width:100%;color:var(--ink);background:var(--field);border-color:var(--field-border)}.need-setup label.need-setup-check{color:var(--muted);text-transform:none;letter-spacing:0;font-size:var(--text-xs);align-items:center;gap:8px;display:flex}.need-setup-footer{justify-content:space-between;align-items:center;gap:16px;display:flex}.need-setup-footer small{color:var(--subtle);font-size:var(--text-xs)}.approval-setting{gap:4px;display:grid}.approval-setting small{color:var(--subtle);font-size:var(--text-xs);padding-left:28px}.approval-setting small.approval-warning{color:var(--amber)}.p-button{font-size:var(--text-xs)!important}.p-button:not(.p-button-text):not(.p-button-link):not(.p-button-danger){box-shadow:var(--raised-shadow);border:1px solid var(--raised-border)!important;background:var(--raised)!important;color:var(--ink)!important}.p-button:not(.p-button-text):not(.p-button-link):not(.p-button-danger):not(:disabled):hover{background:var(--raised-hover)!important}.p-button.main-action:not(.p-button-text):not(.p-button-link):not(.p-button-danger){box-shadow:none;border-color:var(--ink)!important;background:var(--ink)!important;color:#0d0d0d!important}.p-button.main-action:not(.p-button-text):not(.p-button-link):not(.p-button-danger):not(:disabled):hover{background:#fff!important;border-color:#fff!important}.p-button.p-button-text:not(.p-button-danger){color:var(--muted)!important}.p-button.p-button-text:not(.p-button-danger):not(:disabled):hover{color:var(--ink)!important;background:var(--white-6)!important}.list-toolbar{flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:16px;display:flex}.search-input{flex:280px;min-width:220px;max-width:420px}.search-input input{width:100%}.list-select{flex:0 220px;min-width:160px}.filter-chips{flex-wrap:wrap;gap:8px;display:flex}.filter-chips button{border:1px solid var(--border);border-radius:var(--radius-md);min-height:32px;color:var(--muted);background:var(--white-3);font-size:var(--text-xs);cursor:pointer;align-items:center;gap:8px;padding:0 12px;font-weight:600;display:inline-flex}.filter-chips button small{color:var(--subtle);font-size:var(--text-xs);font-variant-numeric:tabular-nums}.filter-chips button.active{color:var(--ink);border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow)}.filter-chips button.active small{color:var(--muted)}.filter-chips button:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.list-toolbar .p-inputtext,.list-toolbar .p-select{color:var(--ink);background:var(--field);border-color:var(--field-border);font-size:var(--text-xs)}.list-toolbar .p-select-label{color:var(--ink);font-size:var(--text-xs)}.list-toolbar .p-select-label.p-placeholder,.list-toolbar .p-inputtext::placeholder,.list-toolbar .p-inputicon,.list-toolbar .p-select-dropdown,.list-toolbar .p-select-clear-icon{color:var(--subtle)}body .p-select-overlay{color:var(--ink);background:var(--night-800);border:1px solid var(--border)}body .p-select-overlay .p-select-option{color:var(--muted);font-size:var(--text-xs)}body .p-select-overlay .p-select-option.p-focus,body .p-select-overlay .p-select-option:hover{color:var(--ink);background:var(--white-8)}body .p-select-overlay .p-select-option.p-select-option-selected{color:var(--ink);background:var(--white-18)}body .p-select-overlay .p-select-header .p-inputtext{color:var(--ink);background:var(--field);border-color:var(--field-border)}.list-paginator .p-paginator{border-top:1px solid var(--white-8);color:var(--muted);background:0 0;border-radius:0}.list-paginator .p-paginator-page,.list-paginator .p-paginator-prev,.list-paginator .p-paginator-next{color:var(--muted)}.list-paginator .p-paginator-page.p-paginator-page-selected{color:var(--ink);background:var(--white-18)}.list-paginator .p-paginator-page:not(.p-paginator-page-selected):hover,.list-paginator .p-paginator-prev:not(.p-disabled):hover,.list-paginator .p-paginator-next:not(.p-disabled):hover{color:var(--ink);background:var(--white-6)}.list-paginator .p-paginator-current{color:var(--subtle);font-size:var(--text-xs)}.table-panel[aria-busy=true] .data-list{opacity:.7;transition:opacity .15s}@media (width<=1100px){.stats-grid,.card-grid{grid-template-columns:repeat(2,1fr)}.capability-review-row{grid-template-columns:minmax(180px,1fr) minmax(170px,.8fr) auto}.capability-review-row .review-actions{grid-column:2/-1}.repository-actions{flex-wrap:wrap}.repository-actions>div{flex-basis:100%;width:100%}}@media (width<=800px){.app-shell{display:block}.sidebar{width:264px;transition:transform .2s;position:fixed;left:0;transform:translate(-105%)}.sidebar.open{transform:translate(0)}.mobile-bar{z-index:25;border-bottom:1px solid var(--divider);background:var(--night-950);justify-content:space-between;align-items:center;gap:12px;height:56px;padding:0 8px 0 16px;display:flex;position:sticky;top:0}.mobile-brand{align-items:center;gap:10px;min-width:0;display:flex}.mobile-brand img{width:22px;height:22px}.mobile-brand strong{font-size:var(--text-md);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.mobile-menu{border-radius:var(--radius-md);width:44px;height:44px;color:var(--ink);background:0 0;border:0;place-items:center;font-size:22px;display:grid}.mobile-backdrop{z-index:19;background:#0000008c;display:block;position:fixed;inset:56px 0 0}.sidebar{z-index:20;height:calc(100vh - 56px);top:56px}.page{padding:16px 16px 48px}.page-header{flex-direction:column;align-items:flex-start}.content-grid,.card-grid{grid-template-columns:1fr}.stats-grid{grid-template-columns:repeat(2,1fr)}.activity-time{display:none}.approval-row{grid-template-columns:minmax(0,1fr) auto 18px}.job-review{padding:16px}.job-review-heading{flex-direction:column;align-items:stretch}.capability-review-row,.job-capability-detail{grid-template-columns:1fr}.capability-review-row .review-actions{grid-column:auto}.repository-setup{grid-template-columns:42px minmax(0,1fr)}.repository-setup-actions{grid-column:1/-1}.setup-actions,.repository-setup-actions{min-width:0}.run-row{grid-template-columns:44px minmax(0,1fr) auto}.run-row>span:nth-child(3){display:none}.repository-actions{flex-direction:column;align-items:stretch}}@media (width<=500px){.search-input,.list-select{flex:100%;min-width:0;max-width:none}.stats-grid{grid-template-columns:1fr}.need-row{grid-template-columns:36px minmax(0,1fr)}.need-row>.status-pill,.need-actions{grid-column:2;justify-self:start}.need-setup.inset,.need-notice{margin-left:0}.need-setup{grid-template-columns:1fr}.need-setup-footer{flex-direction:column;align-items:stretch}.page-header h1{font-size:var(--text-xl)}}.page-notice{border:1px solid color-mix(in srgb, var(--mint) 35%, transparent);border-radius:var(--radius-md);background:var(--white-3);color:var(--ink);font-size:var(--text-sm);align-items:center;gap:8px;margin:0 0 16px;padding:12px 16px;display:flex}.page-notice>.app-icon{color:var(--mint);font-size:16px}.page-notice.error{border-color:color-mix(in srgb, var(--danger) 35%, transparent)}.page-notice.error>.app-icon{color:var(--danger)}.page-notice>span{flex:1;min-width:0}.page-notice-close{border-radius:var(--radius-md);width:26px;height:26px;color:var(--muted);cursor:pointer;background:0 0;border:0;place-items:center;padding:0;display:grid}.page-notice-close:hover{color:var(--ink);background:var(--white-8)}.page-notice.floating{z-index:30;background:var(--raised);max-width:min(440px,100vw - 32px);margin:0;position:fixed;bottom:24px;right:24px;box-shadow:0 12px 32px #00000059}@media (width<=800px){.page-notice.floating{max-width:none;bottom:16px;left:16px;right:16px}.page-notice.floating .page-notice-close{width:40px;height:40px}}.run-status-panel{margin-bottom:16px;padding:20px}.run-status-line{align-items:center;gap:8px;margin-bottom:8px;display:flex}.run-headline{color:var(--ink);font-size:var(--text-md);overflow-wrap:anywhere;margin:0;line-height:1.5}.run-status-panel.failed .run-headline{color:var(--danger)}.run-timeline{margin:0;padding:0;list-style:none;display:grid}.run-timeline li{border-top:1px solid var(--white-8);font-size:var(--text-sm);color:var(--ink);grid-template-columns:90px minmax(0,1fr);gap:16px;padding:12px 4px 12px 16px;display:grid;position:relative}.run-timeline li:before{content:"";background:var(--subtle);border-radius:50%;width:7px;height:7px;position:absolute;top:17px;left:4px}.run-timeline li.warning:before{background:var(--amber)}.run-timeline li.error:before{background:var(--danger)}.run-timeline li.error span{color:var(--danger)}.run-timeline li.pending{color:var(--muted)}.run-timeline li.pending:before{border:1px solid var(--muted);background:0 0}.run-timeline time{color:var(--subtle);font-family:var(--font-mono);font-size:var(--text-xs);font-variant-numeric:tabular-nums;padding-top:1px}.run-timeline span{overflow-wrap:anywhere}.run-output{border-radius:var(--radius-md);background:var(--night-950);max-height:480px;color:var(--ink);font-size:var(--text-xs);white-space:pre-wrap;overflow-wrap:anywhere;margin:0;padding:16px;line-height:1.55;overflow:auto}.segmented{border:1px solid var(--field-border);border-radius:var(--radius-md);background:var(--field);min-height:40px;padding:4px;display:inline-flex}.segmented button{border-radius:var(--radius-sm);color:var(--muted);font-size:var(--text-sm);cursor:pointer;background:0 0;border:0;padding:0 12px;font-weight:600}.segmented button.active{background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink)}.run-file-panel{padding:24px 32px}.run-output-full{max-height:none}.markdown-body{color:var(--ink);font-size:var(--text-md);overflow-wrap:anywhere;line-height:1.65}.markdown-body>:first-child{margin-top:0}.markdown-body>:last-child{margin-bottom:0}.markdown-body h1,.markdown-body h2,.markdown-body h3{margin:1.4em 0 .5em;line-height:1.3}.markdown-body h1{font-size:var(--text-xl)}.markdown-body h2{font-size:var(--text-lg);border-bottom:1px solid var(--border);padding-bottom:8px}.markdown-body h3{font-size:var(--text-md)}.markdown-body p,.markdown-body ul,.markdown-body ol,.markdown-body pre,.markdown-body table,.markdown-body blockquote{margin:0 0 1em}.markdown-body ul,.markdown-body ol{padding-left:1.4em}.markdown-body li+li{margin-top:.25em}.markdown-body a{color:var(--ink);text-decoration:underline;-webkit-text-decoration-color:var(--subtle);text-decoration-color:var(--subtle);text-underline-offset:3px}.markdown-body code{border-radius:var(--radius-sm);background:var(--white-8);padding:1px 4px;font-size:.9em}.markdown-body pre{border-radius:var(--radius-md);background:var(--night-950);padding:16px;overflow:auto}.markdown-body pre code{background:0 0;padding:0}.markdown-body blockquote{border-left:3px solid var(--border);color:var(--muted);padding-left:16px}.markdown-body table{border-collapse:collapse;display:block;overflow-x:auto}.markdown-body th,.markdown-body td{border:1px solid var(--border);padding:8px 12px}.job-skill-panel{margin-bottom:16px;padding:20px;scroll-margin-top:16px}.job-skill-panel .panel-heading p{color:var(--muted);font-size:var(--text-sm);margin:4px 0 0}.job-skill-fields{border:1px solid var(--border);border-radius:var(--radius-md);font-size:var(--text-sm);gap:6px;margin:0 0 16px;padding:12px 16px;display:grid}.job-skill-fields div{grid-template-columns:96px minmax(0,1fr);gap:12px;display:grid}.job-skill-fields dt{color:var(--muted)}.job-skill-fields dd{color:var(--ink);margin:0}.job-skill-frame{max-height:480px;overflow:hidden}.job-skill-frame.clipped{-webkit-mask-image:linear-gradient(#000 calc(100% - 72px),#0000);mask-image:linear-gradient(#000 calc(100% - 72px),#0000)}.job-skill-frame.expanded,.job-skill-frame .run-output{max-height:none}.job-skill-body.markdown-body h1{font-size:var(--text-lg)}.job-skill-body.markdown-body h2{font-size:var(--text-md)}.job-skill-body.markdown-body h3{font-size:var(--text-sm)}.job-skill-toggle{color:var(--muted);font:inherit;font-size:var(--text-sm);cursor:pointer;background:0 0;border:0;align-items:center;gap:6px;margin-top:12px;padding:0;display:inline-flex}.job-skill-toggle:hover,.job-skill-toggle:focus-visible{color:var(--ink)}.job-skill-source{color:var(--muted);overflow-wrap:anywhere;text-decoration:none}.job-skill-source:hover,.job-skill-source:focus-visible{color:var(--ink);text-decoration:underline;-webkit-text-decoration-color:var(--subtle);text-decoration-color:var(--subtle);text-underline-offset:3px}.job-skill-origin code{font-size:var(--text-xs)}.job-skill-source .app-icon{color:var(--subtle);vertical-align:-1px;margin-left:3px;font-size:11px}.job-skill-note{color:var(--muted);font-size:var(--text-sm);margin:12px 0 0}.review-skill-link{color:var(--ink);text-decoration:underline;-webkit-text-decoration-color:var(--subtle);text-decoration-color:var(--subtle);text-underline-offset:3px;white-space:nowrap}@media (width<=720px){.run-file-panel{padding:16px}}.clickable-row{cursor:pointer;transition:background .12s}.clickable-row:hover,.clickable-row:focus-visible{background:var(--white-6);outline:none}.run-row-actions{justify-content:flex-end;align-items:center;gap:8px;display:flex}.file-chip{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--muted);font-size:var(--text-xs);white-space:nowrap;align-items:center;gap:4px;padding:4px 8px;font-weight:600;display:inline-flex}.file-chip:hover{border-color:var(--white-18);color:var(--ink)}.row-chevron{color:var(--subtle);font-size:16px}.clickable-row:hover .row-chevron{color:var(--ink)}@media (width<=800px){.file-chip{gap:0;padding:4px 8px;font-size:0}.file-chip .app-icon{font-size:14px}}.run-stats{flex-direction:column;gap:4px;min-width:0;display:flex}.run-stat-line{font-size:var(--text-xs);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:4px 16px;display:flex}.run-stat{color:var(--ink);white-space:nowrap}.run-meta{color:var(--subtle);font-size:var(--text-xs);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.run-meta code{font-size:var(--text-xs)}.run-statuses{display:flex}.run-row{grid-template-columns:52px minmax(180px,1fr) 150px 96px minmax(220px,1.3fr) 168px}@media (width<=1100px){.run-row{grid-template-columns:52px minmax(160px,1fr) 96px minmax(200px,1.2fr) 168px}.run-row>span:nth-child(3){display:none}.run-stats{display:flex}}@media (width<=800px){.run-row{grid-template-columns:44px minmax(0,1fr) auto;row-gap:8px}.run-stats,.run-row-actions{grid-column:2/-1}.run-row-actions{justify-content:flex-start}.row-chevron{display:none}}.run-failure{min-width:0;font-size:var(--text-xs);align-items:center;gap:8px;display:flex}.run-stats .run-failure{order:-1}.run-stats:has(.run-failure) .run-stat{color:var(--subtle);font-size:var(--text-xs)}.failure-label{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--danger);font-size:var(--text-xs);white-space:nowrap;background:0 0;flex:none;align-items:center;gap:4px;padding:2px 8px 2px 4px;font-weight:600;display:inline-flex}.failure-label .app-icon{font-size:12px}.failure-reason{color:var(--muted);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.run-failure.canceled .failure-label{border-color:var(--border);background:var(--white-6);color:var(--muted)}.run-failure-headline{flex-wrap:wrap;align-items:center;gap:8px;display:flex;color:var(--ink)!important}.run-next-step{color:var(--muted);font-size:var(--text-sm);flex-wrap:wrap;align-items:center;gap:8px 16px;margin:8px 0 0;display:flex}.run-failure-headline .failure-label{font-size:var(--text-xs);padding:4px 8px}.run-status-line{color:var(--ink);font-size:var(--text-sm);font-variant-numeric:tabular-nums;flex-wrap:wrap;gap:8px 16px;margin-bottom:0}.run-status-line .run-stat{color:var(--ink)}.run-status-line .run-stat.warn{color:var(--amber)}.run-stat-quiet{color:var(--subtle);font-size:var(--text-xs)}.run-status-panel .run-headline{margin-top:12px}.run-active-note{align-items:center;gap:8px;display:flex;color:var(--muted)!important;font-size:var(--text-sm)!important}.run-layout{grid-template-columns:minmax(0,1fr) 300px;align-items:start;gap:16px;display:grid}.run-main{grid-template-columns:minmax(0,1fr);gap:16px;min-width:0;display:grid}.run-main>*{min-width:0}.run-main>.job-runs-panel{margin-bottom:0}.run-report-actions{font-size:var(--text-sm);gap:16px;display:flex}.run-report{position:relative}.run-report.collapsed{max-height:640px;overflow:hidden;-webkit-mask-image:linear-gradient(#000 82%,#0000);mask-image:linear-gradient(#000 82%,#0000)}.run-report-toggle{border:1px solid var(--raised-border);border-radius:var(--radius-md);background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink);font-size:var(--text-sm);cursor:pointer;align-items:center;gap:4px;margin-top:12px;padding:8px 12px;font-weight:600;display:inline-flex}.run-timeline-fold{padding:16px 24px}.run-timeline-fold summary{color:var(--muted);font-size:var(--text-sm);cursor:pointer;font-weight:600}.run-timeline-fold[open] summary{margin-bottom:8px}.run-details{padding:20px 24px;position:sticky;top:24px}.run-details h2{font-size:var(--text-md);margin-bottom:12px}.run-details dl{gap:12px;margin:0;display:grid}.run-details dl>div{gap:2px;display:grid}.run-details dt{color:var(--subtle);font-size:var(--text-xs)}.run-details dd{overflow-wrap:anywhere;min-width:0;font-size:var(--text-sm);margin:0}.run-details code{font-size:var(--text-xs)}.run-details-muted{color:var(--subtle)}@media (width<=1100px){.run-layout{grid-template-columns:minmax(0,1fr)}.run-details{position:static}}.cell-sub{color:var(--subtle);font-size:var(--text-xs);margin-top:2px;display:block}.cell-sub.last-run:hover{color:var(--ink)}.cell-sub.last-run.failed{color:var(--danger)}.job-status-strip{flex-wrap:wrap;align-items:center;gap:16px 32px;margin-bottom:16px;padding:16px 24px;display:flex}.job-status-item{flex-direction:column;gap:2px;min-width:0;display:flex}.job-status-item>span{color:var(--subtle);font-size:var(--text-xs)}.job-status-item>strong{font-size:var(--text-md)}.job-status-item>small{color:var(--subtle);font-size:var(--text-xs)}.job-last-run{color:var(--ink);font-size:var(--text-md);align-items:center;gap:8px;font-weight:600;display:inline-flex}.job-last-run:hover{color:var(--ink)}.job-status-action{color:var(--ink);font-size:var(--text-sm);align-items:center;gap:8px;margin-left:auto;font-weight:600;display:inline-flex}.job-status-warning{color:var(--amber);font-size:var(--text-sm);align-items:center;gap:8px;font-weight:600;display:inline-flex}.run-outcomes{padding:20px}.outcome-strip{flex-wrap:wrap;gap:4px;margin:0 0 20px;padding:0;list-style:none;display:flex}.outcome-mark{border-radius:var(--radius-sm);background:var(--white-6);width:22px;height:22px;color:var(--muted);font-size:var(--text-sm);place-items:center;transition:transform .12s;display:grid}.outcome-mark:hover,.outcome-mark:focus-visible{box-shadow:0 0 0 2px var(--night-850), 0 0 0 4px var(--white-18);outline:none;transform:translateY(-2px)}.outcome-mark.succeeded{background:var(--white-6);color:var(--mint)}.outcome-mark.failed{background:var(--white-6);color:var(--danger)}.outcome-mark.running,.outcome-mark.queued{background:var(--white-6);color:var(--ink)}.outcome-tiles{border:1px solid var(--card-border);border-radius:var(--radius-md);background:var(--card-border);grid-template-columns:repeat(4,minmax(0,1fr));gap:1px;display:grid;overflow:hidden}.outcome-tile{background:var(--night-900);flex-direction:column;gap:4px;min-width:0;padding:16px;display:flex}.outcome-tile>span{color:var(--subtle);font-size:var(--text-xs);font-weight:600}.outcome-tile>strong{font-size:var(--text-xl);font-variant-numeric:tabular-nums;font-weight:500}.outcome-tile>small{color:var(--subtle);font-size:var(--text-xs)}.sparkline{max-width:100%;margin-top:2px;display:block;overflow:visible}.sparkline path{fill:var(--subtle)}.sparkline g:hover path{fill:var(--ink)}.run-row.without-job{grid-template-columns:52px 130px 104px minmax(200px,1fr) 168px}@media (width<=1100px){.outcome-tiles{grid-template-columns:repeat(2,minmax(0,1fr))}.run-row.without-job{grid-template-columns:52px 110px 104px minmax(160px,1fr) 168px}.run-row.without-job>span:nth-child(2){display:inline}}@media (width<=800px){.run-row.without-job{grid-template-columns:44px minmax(0,1fr) auto}.run-row.without-job>.run-when{display:none}.job-status-action{margin-left:0}}.outcome-tile>span em{font-style:normal;font-weight:500}.activity-main .activity-failure{color:var(--muted)}.upcoming-panel{padding:20px}.upcoming-list{display:grid}.upcoming-row{border-top:1px solid var(--white-8);border-radius:var(--radius-md);align-items:center;gap:12px;padding:12px 4px;display:flex}.upcoming-row:hover{background:var(--white-6)}.upcoming-main{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.upcoming-row:hover .row-chevron{color:var(--ink)}.upcoming-row strong{font-size:var(--text-sm)}.upcoming-row span{color:var(--muted);font-size:var(--text-xs)}.upcoming-row small{color:var(--subtle);font-size:var(--text-xs);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.stat-card.linked{color:inherit;transition:background .15s;display:block}.stat-card.linked:hover{background:var(--night-850)}.stat-card.linked:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}.stat-arrow{float:right;color:var(--subtle);opacity:0;font-size:16px;transition:opacity .15s}.stat-card.linked:hover .stat-arrow,.stat-card.linked:focus-visible .stat-arrow{opacity:1;color:var(--ink)}.spend-panel{margin-top:16px;padding:20px}.spend-list{display:grid}.spend-row{border-top:1px solid var(--white-8);border-radius:var(--radius-md);color:inherit;grid-template-columns:minmax(160px,1fr) minmax(80px,1.2fr) 110px 80px 18px;align-items:center;gap:16px;padding:12px 4px;display:grid}.spend-row.single{grid-template-columns:minmax(0,1fr) 80px 18px}.spend-row:hover{background:var(--white-6)}.spend-row:hover .row-chevron{color:var(--ink)}.spend-bar{border-radius:var(--radius-sm);background:var(--white-6);height:6px;overflow:hidden}.spend-bar>span{border-radius:var(--radius-sm);background:var(--muted);height:100%;display:block}.spend-share{color:var(--subtle);font-size:var(--text-xs);font-variant-numeric:tabular-nums;text-align:right}.spend-value{font-size:var(--text-sm);text-align:right;font-variant-numeric:tabular-nums}@media (width<=800px){.spend-row{grid-template-columns:minmax(0,1fr) auto}.spend-bar{grid-area:2/1/auto/-1}.spend-share,.spend-row .row-chevron{display:none}}.repository-alert{align-items:center;gap:16px;margin-bottom:16px;padding:16px 20px;display:flex}.repository-alert>.app-icon{color:var(--amber);font-size:22px}.repository-alert.error>.app-icon{color:var(--danger)}.repository-alert>div{flex-direction:column;flex:1;gap:2px;min-width:0;display:flex}.repository-alert strong{font-size:var(--text-md)}.repository-alert span{color:var(--muted);font-size:var(--text-xs)}.repository-info-card{margin-bottom:16px;padding:20px}.repository-statuses{gap:8px;display:flex}.repository-metadata.compact-grid{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:4px 24px;display:grid}.repository-metadata.compact-grid>div{border-top:0;grid-template-columns:1fr;gap:2px;padding:8px 0}.repository-metadata.compact-grid>div.full-row{grid-column:1/-1}.repository-danger{color:var(--muted);font-size:var(--text-xs);flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;padding:4px 0 8px;display:flex}.p-button.p-button-danger:not(.p-button-text):not(.p-button-outlined){border-color:var(--danger)!important;background:var(--danger)!important;color:#2a0a12!important}.p-button.p-button-danger:not(.p-button-text):not(.p-button-outlined):not(:disabled):hover{background:#ff8fa0!important;border-color:#ff8fa0!important}.quiet-line{color:var(--subtle);font-size:var(--text-xs);flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 16px;padding:4px 8px;display:flex}.quiet-line>.app-icon{color:var(--mint);font-size:16px}.quiet-line code{font-size:var(--text-xs)}.check-age{color:var(--subtle);margin-left:8px}.settings-members{margin-bottom:16px;padding:20px}.member-list{margin:0;padding:0;list-style:none;display:grid}.member-list li{border-top:1px solid var(--white-8);align-items:center;gap:12px;padding:8px 2px;display:flex}.avatar.small{background:var(--night-750);width:28px;height:28px;color:var(--ink);border:0;border-radius:50%;font-size:12px}.member-email{text-overflow:ellipsis;min-width:0;font-size:var(--text-sm);flex:1;overflow:hidden}.env-chip{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--subtle);font-family:var(--font-mono);font-size:var(--text-xs);cursor:pointer;background:0 0;align-items:center;gap:4px;padding:4px 8px;display:inline-flex}.env-chip:hover{border-color:var(--white-18);color:var(--ink)}.need-setup{align-items:start}.status-pill.optional{--dot:var(--subtle);color:var(--muted)}.need-setup label{align-content:start;min-width:0}.need-setup .p-inputtext{min-width:0}.need-setup .p-inputtext:enabled:focus{border-color:var(--ink);outline-color:var(--focus)}.need-setup .p-inputtext:disabled{background:var(--night-800);color:var(--muted)}.need-setup input[type=checkbox]{accent-color:var(--ink)}.need-setup small a{color:var(--ink);text-underline-offset:3px;font-weight:600;text-decoration:underline}.need-setup small a:hover{color:var(--ink)}.need-setup small a:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:2px}.need-row{grid-template-columns:32px minmax(0,1fr) 140px 176px}.need-row>.status-pill{justify-self:start}.need-row>.need-actions{flex-wrap:nowrap}.need-primary>span,.need-primary>small{overflow-wrap:anywhere}.need-actions{flex-wrap:wrap}.need-setup-footer>small{min-width:0;line-height:1.5}.webhook-sample{border:1px solid var(--border);border-radius:var(--radius-md)}.webhook-sample>summary{color:var(--ink);font-size:var(--text-sm);cursor:pointer;align-items:center;gap:8px;padding:12px;font-weight:600;list-style:none;display:flex}.webhook-sample>summary::-webkit-details-marker{display:none}.webhook-sample>summary .app-icon{transition:transform .15s}.webhook-sample[open]>summary .app-icon{transform:rotate(90deg)}.webhook-sample-body{color:var(--muted);font-size:var(--text-xs);gap:12px;padding:0 12px 12px;line-height:1.5;display:grid}.webhook-sample-body ul{margin:0;padding-left:1.2em}.webhook-sample-body .run-output{max-height:320px}.webhook-sample-body a{color:var(--ink);align-items:center;gap:4px;font-weight:600;display:inline-flex}.webhook-sample-loading{align-items:center;gap:8px;margin:0;display:flex}.need-setup-footer>.need-actions{flex-shrink:0}.job-row>div{min-width:0}.job-actions{justify-content:flex-end;gap:8px}.job-action{border:1px solid var(--border);border-radius:var(--radius-md);width:34px;height:34px;color:var(--muted);cursor:pointer;background:0 0;flex:0 0 34px;justify-content:center;align-items:center;padding:0;transition:color .15s,border-color .15s,background .15s;display:inline-flex}.job-action>.app-icon{font-size:18px;line-height:1;display:block}.job-action.labeled{width:auto;font-size:var(--text-xs);flex:none;gap:6px;padding:0 10px}.job-action.labeled>.app-icon{font-size:16px}.job-action:hover:not(:disabled),.job-action.active{color:var(--ink);border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow)}.job-action:focus-visible{outline:2px solid var(--focus);outline-offset:3px}.job-action:disabled{opacity:.4;cursor:not-allowed}.job-action.is-loading>.app-icon{animation:1s linear infinite job-action-spin}.job-action.warn{position:relative}@keyframes job-action-spin{to{transform:rotate(360deg)}}.job-review{padding:20px 24px 24px}.job-review-heading{align-items:flex-start;margin-bottom:16px}.job-review-heading p{margin-bottom:0;line-height:1.5}.job-review-heading>.p-button{flex-shrink:0}.page-warning{border:1px solid color-mix(in srgb, var(--amber) 35%, transparent);border-radius:var(--radius-md);background:var(--white-3);color:var(--ink);font-size:var(--text-sm);flex-wrap:wrap;align-items:center;gap:8px 12px;margin:0 0 16px;padding:12px 16px;display:flex}.page-warning>.app-icon{color:var(--amber)}.page-warning>span{flex:240px}.capability-review-row{grid-template-columns:minmax(0,1fr) minmax(180px,1fr) auto auto;gap:16px;padding:16px 0}.capability-review-row>*{min-width:0}.capability-summary{overflow-wrap:anywhere;gap:4px}.capability-summary span,.capability-summary small{margin-top:0;line-height:1.5}.connection-required{overflow-wrap:anywhere}.review-actions{justify-content:flex-end;align-items:center;gap:8px}.review-actions>.p-button{white-space:nowrap;flex-shrink:0;gap:8px}@media (width>=801px) and (width<=1200px){.capability-review-row{grid-template-columns:minmax(0,1fr) minmax(180px,1fr) auto}.capability-review-row .review-actions{grid-column:2/-1}}@media (width<=800px){.job-review{padding:16px}.capability-review-row{grid-template-columns:minmax(0,1fr) auto}.capability-review-row>.capability-summary,.capability-review-row>.connection-select,.capability-review-row>.connection-required{grid-column:1/-1}.capability-review-row>.review-actions{grid-column:2}}.job-name-link{max-width:100%}.job-name-link>strong{overflow-wrap:anywhere}.job-primary>span{white-space:nowrap}.capability-summary,.connection-select{min-width:0}.capability-summary strong{overflow-wrap:anywhere}@media (width<=800px){.need-setup{grid-template-columns:1fr}.need-setup-footer{flex-direction:column;align-items:stretch}.need-row{grid-template-columns:36px minmax(0,1fr)}.need-row>.status-pill,.need-row>.need-actions{grid-column:2;justify-self:start}.need-row>.need-actions{flex-wrap:wrap}}.run-limits{margin-bottom:16px;padding:20px}.run-limit-grid{grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:16px 20px;margin-top:12px;display:grid}.run-limit{align-content:start;gap:4px;display:grid}.run-limit>label{color:var(--subtle);font-size:var(--text-xs);font-weight:600}.run-limit>strong{font-size:var(--text-md)}.run-limit small{color:var(--muted);font-size:var(--text-xs)}.run-limit small.field-error{color:var(--danger)}.run-limit .run-limit-help{color:var(--subtle)}.run-limit-input{align-items:center;gap:8px;display:flex}.run-limit-input input{width:100%;min-width:0}.run-limit-input span{color:var(--muted);font-size:var(--text-xs)}.run-limit-wide{grid-column:1/-1;margin:0}.run-limit-footer{border-top:1px solid var(--white-8);flex-wrap:wrap;justify-content:space-between;align-items:center;gap:8px;padding-top:12px;display:flex}.run-limit-footer small{color:var(--muted);font-size:var(--text-xs)}.invite-prompt-wrap{width:min(1440px,100%);margin:0 auto;padding:24px 32px 0}.invite-prompt-wrap+.page{padding-top:16px}.invite-prompt{border:1px solid var(--white-12);border-radius:var(--radius-lg);background:var(--white-8);color:var(--ink);font-size:var(--text-sm);align-items:center;gap:12px;padding:12px 16px;display:flex}.invite-prompt>.app-icon{color:var(--ink);font-size:20px}.invite-prompt>div{flex:1;gap:2px;min-width:0;display:grid}.invite-prompt>div>span{color:var(--muted);font-size:var(--text-xs)}.invite-prompt>div>.invite-prompt-error{color:var(--danger)}@media (width<=640px){.invite-prompt-wrap{padding:16px 16px 0}.invite-prompt{flex-wrap:wrap}.invite-prompt>div{flex-basis:calc(100% - 32px)}}.invites{border-top:1px solid var(--white-8);gap:12px;margin-top:16px;padding-top:16px;display:grid}.invite-form{flex-wrap:wrap;gap:8px;display:flex}.invite-form .invite-role{min-width:240px}.invite-form .p-inputtext{flex:1;min-width:200px}.invite-link{border:1px solid color-mix(in srgb, var(--mint) 35%, transparent);border-radius:var(--radius-md);background:var(--white-3);font-size:var(--text-xs);justify-items:start;gap:8px;padding:12px 16px;display:grid}.invite-link code{overflow-wrap:anywhere;max-width:100%;font-size:var(--text-xs)}.invite-list li:first-child{border-top:0}.invite-confirm{gap:4px;display:inline-flex}.member-email small{color:var(--subtle);font-size:var(--text-xs);display:block}.member-list .p-select.member-role{width:128px}.member-list .p-button{min-width:84px}@media (width<=560px){.member-list li{flex-wrap:wrap;row-gap:8px}.member-email{overflow-wrap:anywhere;flex-basis:calc(100% - 40px)}.member-list .p-select.member-role{flex:1;width:auto;margin-left:40px}}.no-workspace{flex-direction:column;justify-content:center;min-height:100vh;padding:24px 16px;display:flex}.no-workspace-card{flex-direction:column;align-items:flex-start;width:min(520px,100%);margin:0 auto;display:flex}.no-workspace-card>img{width:56px;margin-bottom:28px}.no-workspace-card h1{font-size:var(--text-2xl);letter-spacing:-.04em;margin:8px 0 16px;line-height:1.08}.no-workspace-card p{color:var(--muted);margin:0 0 24px;line-height:1.7}.no-workspace-create{margin-bottom:24px}.no-workspace .invite-prompt-wrap{width:min(720px,100%);margin:0 auto 32px;padding:0}.app-icon{vertical-align:-.125em;flex-shrink:0;width:1em;height:1em;display:inline-block}.app-icon.line{fill:none;stroke:currentColor;stroke-width:1.75px;stroke-linecap:round;stroke-linejoin:round}.app-icon.logo{fill:currentColor}.app-icon-spin{animation:1s linear infinite icon-spin}@keyframes icon-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.app-icon-spin,.job-action.is-loading>.app-icon{animation:none}}.runs-panel{margin-bottom:16px;padding:20px}.runs-chart{margin:0;position:relative}.runs-chart svg{width:100%;display:block;overflow:visible}.runs-chart-legend{color:var(--muted);font-size:var(--text-xs);flex-wrap:wrap;gap:8px 20px;margin-bottom:16px;display:flex}.legend-item{align-items:center;gap:8px;display:inline-flex}.legend-item strong{color:var(--ink);font-variant-numeric:tabular-nums;font-weight:600}.legend-dot{background:var(--subtle);border-radius:50%;width:6px;height:6px}.legend-item.succeeded .legend-dot,.runs-chart rect.succeeded{fill:var(--mint);background:var(--mint)}.legend-item.failed .legend-dot,.runs-chart rect.failed{fill:var(--danger);background:var(--danger)}.legend-item.canceled .legend-dot,.runs-chart rect.canceled{fill:var(--subtle);background:var(--subtle)}.runs-chart text{fill:var(--subtle);font-size:var(--text-xs);font-variant-numeric:tabular-nums}.runs-chart-grid line{stroke:var(--white-6)}.runs-chart-weeks line{stroke:var(--white-12);stroke-dasharray:2 4}.runs-chart rect.previous{fill:var(--subtle);opacity:.45}.legend-period{min-width:88px;color:var(--ink);font-weight:500}.runs-chart-hit{fill:#0000}.runs-chart-day:hover .runs-chart-hit{fill:var(--white-3)}.runs-chart-day:hover text{fill:var(--ink)}.runs-chart-empty{color:var(--subtle);font-size:var(--text-xs);place-items:center;margin:0;display:grid;position:absolute;inset:40px 0 24px 32px}.row-glyph{border:1px solid var(--card-border);border-radius:var(--radius-md);width:32px;height:32px;color:var(--muted);place-items:center;font-size:16px;display:grid}.need.optional .need-primary>strong{color:var(--muted)}.heading-count{color:var(--subtle);font-size:var(--text-sm);font-variant-numeric:tabular-nums;margin-left:8px;font-weight:400}.need-primary .mono-line{color:var(--subtle);font-family:var(--font-mono);font-size:var(--text-xs);text-overflow:ellipsis;overflow:hidden}.need-primary .mono-line .link-button{font-family:var(--font-sans);font-size:var(--text-xs);margin-left:4px}.need-primary .served{color:var(--muted)}.need-primary .served code{color:var(--ink);font-size:var(--text-xs)}.need-primary>strong.connection-name{font-family:var(--font-sans);font-weight:500}.icon-action{border-radius:var(--radius-md);width:30px;height:30px;color:var(--muted);cursor:pointer;background:0 0;border:1px solid #0000;place-items:center;padding:0;font-size:16px;display:inline-grid}.icon-action:hover,.icon-action[aria-expanded=true]{border-color:var(--raised-border);background:var(--raised);box-shadow:var(--raised-shadow);color:var(--ink)}.icon-action:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.confirm-strip{border:1px solid color-mix(in srgb, var(--danger) 35%, transparent);border-radius:var(--radius-md);color:var(--muted);font-size:var(--text-xs);flex-wrap:wrap;align-items:center;gap:8px 12px;margin:0 4px 12px 52px;padding:8px 8px 8px 12px;display:flex}.confirm-strip>span{flex:280px}body .row-menu{border:1px solid var(--raised-border);border-radius:var(--radius-md);background:var(--night-850);min-width:200px;padding:4px;box-shadow:0 12px 32px #00000080}body .row-menu .p-menu-list{gap:2px}body .row-menu .p-menu-item-content{border-radius:var(--radius-sm);background:0 0}body .row-menu .p-menu-item.p-focus .p-menu-item-content,body .row-menu .p-menu-item-content:hover{background:var(--white-6)}body .row-menu .p-menu-separator{border-top-color:var(--card-border);margin:4px 8px}.row-menu-item{color:var(--ink);font-size:var(--text-sm);align-items:center;gap:10px;padding:8px 10px;display:flex}.row-menu-item .app-icon{color:var(--muted);font-size:16px}.row-menu-item.danger,.row-menu-item.danger .app-icon{color:var(--danger)}@media (width<=800px){.runs-toolbar{flex-wrap:wrap}.runs-toolbar .search-input{flex-basis:100%}.runs-toolbar .list-select{flex:1 1 0}.list-head{padding:8px}.list-head .sort-select{width:136px}.page-header.compact{flex-flow:wrap;align-items:center;gap:12px}.page-header.compact>div:first-child{flex:200px}.stats-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.stat-card{padding:12px 14px}.stat-card>small{font-size:var(--text-xs)}}.need-group-label{border-top:1px solid var(--divider);color:var(--subtle);font-size:var(--text-xs);margin:8px 0 0;padding:12px 4px 4px}.need-job{color:var(--muted);text-decoration:underline;-webkit-text-decoration-color:var(--white-18);text-decoration-color:var(--white-18);text-underline-offset:3px}.need-job:hover{color:var(--ink);text-decoration-color:currentColor}.repository-row{border-top:1px solid var(--divider);color:inherit;grid-template-columns:32px minmax(0,1fr) 72px 140px 112px 16px;align-items:center;gap:16px;padding:12px 20px;display:grid}.repository-row:first-child{border-top:0}.repository-row:hover{background:var(--white-6)}.repository-row-main{flex-direction:column;gap:2px;min-width:0;display:flex}.repository-row-main strong{font-size:var(--text-md);text-overflow:ellipsis;white-space:nowrap;font-weight:500;overflow:hidden}.repository-row-main span{color:var(--subtle);font-size:var(--text-xs);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.repository-row-jobs,.repository-row-sync{color:var(--muted);font-size:var(--text-xs);font-variant-numeric:tabular-nums;white-space:nowrap}.repository-row .row-chevron{color:var(--subtle)}@media (width<=800px){.repository-row{grid-template-columns:32px minmax(0,1fr) auto;padding:12px 16px}.repository-row-jobs,.repository-row-sync,.repository-row .row-chevron{display:none}}.job-row{--job-line:20px;--job-sub-line:16px;min-height:calc(29px + var(--job-line) + 2px + var(--job-sub-line));grid-template-columns:minmax(0,1.4fr) minmax(0,1.2fr) 112px 168px;padding:14px 20px}.job-row .job-name-link,.job-row .job-name-link>strong,.job-row .job-cell>strong,.job-row .job-primary>span,.job-row .cell-sub{white-space:nowrap;text-overflow:ellipsis;min-width:0;max-width:100%;overflow:hidden}.job-row .job-primary strong,.job-row .job-cell strong{line-height:var(--job-line)}.job-row .job-primary>span,.job-row .cell-sub{line-height:var(--job-sub-line);margin-top:2px}.job-row .job-cell{gap:0}.job-group-head{border-bottom:1px solid var(--white-8);background:var(--white-3);color:var(--subtle);font-size:var(--text-xs);padding:6px 20px;font-weight:500}.job-group-head a:hover{color:var(--ink)}.job-review-heading .review-links{white-space:nowrap}.job-action.labeled{white-space:nowrap;flex-shrink:0;font-weight:500}.job-action.attention{border-color:color-mix(in srgb, var(--amber) 45%, transparent);background:color-mix(in srgb, var(--amber) 10%, transparent);color:var(--amber)}.job-action.attention:hover:not(:disabled),.job-action.attention.active{border-color:color-mix(in srgb, var(--amber) 70%, transparent);background:color-mix(in srgb, var(--amber) 16%, transparent);color:var(--amber);box-shadow:none}.job-access-review .connection-setup-action,.job-access-review .review-actions>.p-button{height:32px;min-height:32px;padding:0 12px;font-weight:500}.job-start-note{color:var(--subtle);font-size:var(--text-xs);line-height:var(--job-sub-line);display:none}.job-when{gap:0 6px;display:flex}.job-when>*{flex-shrink:0}.job-when>:last-child{text-overflow:ellipsis;flex-shrink:1;min-width:0;overflow:hidden}.job-when>*+:before{content:"·";color:var(--subtle);margin-right:6px}.job-when .last-run{color:inherit}.job-when .last-run:hover{color:var(--ink)}.job-when .last-run.failed{color:var(--danger)}@media (width<=800px){.job-row{grid-template-columns:minmax(0,1fr) auto;grid-template-rows:var(--job-line) var(--job-sub-line);row-gap:2px}.job-row>.job-cell,.job-row .job-primary>span{display:none}.job-row .job-start-note{display:block}.job-row .job-action.labeled{justify-content:center;width:40px;padding:0}.job-row .job-action.labeled>span{clip-path:inset(50%);white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}.job-row>.job-statuses{grid-area:2/1;gap:8px;display:flex}.job-row>.job-actions{grid-area:1/2/span 2}}.job-cell{flex-direction:column;gap:2px;min-width:0;display:flex}.job-cell strong{font-size:var(--text-sm);font-weight:500}.job-row .job-primary strong{font-size:var(--text-md);font-weight:500}.job-row .job-actions{justify-content:flex-end}.server-settings{margin-bottom:16px;padding:20px 20px 8px}.server-group{border-top:1px solid var(--divider);grid-template-columns:160px minmax(0,1fr);gap:16px;padding:12px 0;display:grid}.server-group h3{color:var(--muted);font-size:var(--text-sm);margin:10px 0 0;font-weight:500}.server-group dl{margin:0;display:grid}.server-row{grid-template-columns:180px minmax(0,1fr);align-items:baseline;gap:16px;padding:10px 0;display:grid}.server-row+.server-row{border-top:1px dashed var(--divider)}.server-row dt{color:var(--subtle);font-size:var(--text-sm)}.server-row dd{flex-direction:column;align-items:flex-start;gap:2px;min-width:0;margin:0;display:flex}.server-row .env-chip{margin-top:6px}.server-row dd strong{font-size:var(--text-sm);overflow-wrap:anywhere;font-weight:500}.server-row dd small{color:var(--subtle);font-size:var(--text-xs)}@media (width<=800px){.server-group,.server-row{grid-template-columns:1fr;gap:4px}}body .p-inputtext,body .p-select{border-radius:var(--radius-md);background:var(--field);border-color:var(--field-border);color:var(--ink);font-size:var(--text-sm)}body .p-inputtext{padding:8px 10px}body .p-select-label{font-size:var(--text-sm);padding:8px 10px}body .workspace-menu{width:224px}.menu-heading{color:var(--subtle);font-size:var(--text-xs);padding:8px 10px 6px}.workspace-menu .menu-heading{border-bottom:0;margin-bottom:0}.workspace-option .avatar.small{width:20px;height:20px;font-size:10px}.workspace-option-name{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.workspace-option-role{color:var(--subtle);font-size:var(--text-xs)}.workspace-current-check{color:var(--ink);font-size:var(--text-md)}@media (width<=800px){.list-head{flex-wrap:wrap}.list-head .filter-chips{flex-wrap:wrap;overflow:visible}.list-head .filter-chips button{min-height:36px}.icon-action,.job-action{width:40px;height:40px}.job-action{flex-basis:40px}.job-action.labeled{flex-basis:auto;width:auto}.crumbs a,.panel-heading>a{align-items:center;min-height:32px;display:inline-flex}.p-button.p-button-sm{min-height:36px}}@media (hover:none) and (pointer:coarse){input:not([type=checkbox]):not([type=radio]):not([type=range]),textarea,select,body .p-inputtext{font-size:var(--text-lg)!important}}.api-tokens{margin-bottom:16px;padding:20px}.token-list .default-badge{margin-left:8px}.token-empty{border-top:1px solid var(--white-8);color:var(--subtle);font-size:var(--text-xs);margin:0;padding:12px 2px}.token-form{grid-template-columns:minmax(200px,1fr) minmax(240px,auto) 120px auto;gap:8px;display:grid}.token-note{color:var(--amber);font-size:var(--text-xs);align-items:center;gap:8px;margin:0;display:flex}@media (width<=900px){.token-form{grid-template-columns:1fr 1fr}.token-form .token-name,.token-form .p-button{grid-column:1/-1}}@media (width<=560px){.token-form{grid-template-columns:1fr}.token-list .member-email{flex:1 1 0}}.tool-rule-grid{gap:8px;margin-top:4px;display:grid}.tool-rule-tool{gap:4px;display:grid}.tool-rule-tool>code{color:var(--ink);font-size:var(--text-xs)}.tool-rules{gap:4px;display:grid}.tool-rule-list{flex-wrap:wrap;gap:4px;margin:0;padding:0;list-style:none;display:flex}.tool-rule-list li{color:var(--ink);background:var(--white-6);font-size:var(--text-xs);border-radius:999px;align-items:center;gap:4px;padding:2px 8px;display:inline-flex}.tool-rule-list .app-icon{width:12px;height:12px;color:var(--muted)}.tool-rule-remove{color:var(--muted);cursor:pointer;background:0 0;border:0;padding:0;display:inline-flex}.tool-rule-remove:hover{color:var(--ink)}.tool-rule-add{font-size:var(--text-xs);justify-self:start;align-items:center;gap:4px;display:inline-flex}.tool-rule-add .app-icon{width:12px;height:12px}.tool-rule-form{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.tool-rule-form .p-select{min-width:140px;font-size:var(--text-xs)}.tool-rule-form .p-inputtext{min-height:30px;font-size:var(--text-xs);flex:180px;padding:4px 8px}.job-triggers-panel{margin-bottom:16px;padding:20px}.job-trigger{gap:12px;display:grid}.job-trigger-actions{align-items:center;gap:4px;display:inline-flex}.job-trigger-url{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--white-3);align-items:center;gap:12px;padding:8px 12px;display:flex}.job-trigger-url>span{color:var(--subtle);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em}.job-trigger-url code{overflow-wrap:anywhere;min-width:0;font-size:var(--text-xs);flex:1}.job-trigger-note{color:var(--subtle);font-size:var(--text-xs);margin:0}.job-trigger-events h3{font-size:var(--text-xs);color:var(--muted);margin:4px 0 8px}.job-trigger-events ul{margin:0;padding:0;list-style:none;display:grid}.job-trigger-events li{border-top:1px solid var(--white-8);font-size:var(--text-xs);grid-template-columns:84px minmax(0,1fr) auto;align-items:center;gap:12px;padding:8px 4px;display:grid}.job-trigger-reason{overflow-wrap:anywhere;min-width:0}.job-trigger-events small{color:var(--subtle);font-size:var(--text-xs);white-space:nowrap}.job-trigger-rules{gap:8px;display:grid}.job-trigger-rules h3{font-size:var(--text-xs);color:var(--muted);margin:0}.job-trigger-rules ul{font-size:var(--text-sm);margin:0;padding-left:18px}.job-trigger-rules p{font-size:var(--text-sm);color:var(--muted);margin:0}.job-trigger-cap{gap:4px;max-width:420px;display:grid}.job-trigger-cap label{font-size:var(--text-xs);color:var(--muted)}.job-trigger-cap-input{align-items:center;gap:8px;display:flex}.job-trigger-cap-input input{width:120px}.job-trigger-count{color:var(--subtle);font-size:var(--text-xs);margin-left:8px}.job-trigger-cap small{color:var(--subtle);font-size:var(--text-xs)}@media (width<=640px){.job-trigger-url{flex-wrap:wrap}.job-trigger-events li{grid-template-columns:1fr auto}.job-trigger-events li>.status-pill{grid-column:1/-1;justify-self:start}}.source-access-wrap{font-size:var(--text-xs);color:var(--muted);margin:-8px 4px 16px 52px}.source-access-wrap summary{cursor:pointer;width:fit-content}.source-access-wrap summary:hover{color:var(--ink)}details.source-access-wrap[open] .source-access{margin-top:8px}.source-access{font-size:var(--text-xs);color:var(--muted);gap:6px;margin:0;padding:0;list-style:none;display:grid}.source-access li{align-items:center;gap:8px;display:flex}.source-access strong{min-width:132px;color:var(--ink);font-weight:600}.source-access li.ok .app-icon{color:var(--mint)}.source-access li.gap .app-icon,.source-access li.gap span{color:var(--amber)}.source-access-fix a{align-items:center;gap:4px;display:inline-flex}@media (width<=720px){.source-access-wrap{margin-left:0}.source-access li{flex-wrap:wrap}.source-access strong{min-width:0}}.setup-checklist{margin-bottom:16px;padding:20px 20px 8px}.setup-heading{justify-content:space-between;align-items:center;gap:16px;margin-bottom:12px;display:flex}.setup-heading h2{font-size:var(--text-lg);margin:0}.setup-heading p{color:var(--muted);font-size:var(--text-xs);margin:4px 0 0}.setup-checklist ol{margin:0;padding:0;list-style:none;display:grid}.setup-checklist li{border-top:1px solid var(--white-8);grid-template-columns:28px minmax(0,1fr) auto;align-items:center;gap:14px;min-height:64px;padding:12px 4px;display:grid}.setup-mark{border:1px solid var(--border);width:26px;height:26px;color:var(--muted);font-size:var(--text-xs);border-radius:50%;place-items:center;display:grid}.setup-checklist li.current .setup-mark{color:var(--amber);border-color:var(--amber)}.setup-checklist li.done .setup-mark{color:var(--mint);background:var(--white-6);border-color:#0000}.setup-copy{flex-direction:column;gap:4px;min-width:0;display:flex}.setup-copy strong{font-size:var(--text-sm)}.setup-copy span{max-width:64ch;color:var(--muted);font-size:var(--text-xs);line-height:1.5}.setup-checklist li.done .setup-copy strong,.setup-checklist li.upcoming .setup-copy strong{color:var(--muted)}.setup-checklist li.upcoming .setup-copy span{color:var(--subtle)}.setup-actions{flex-wrap:wrap;justify-content:flex-end;gap:8px;display:flex}@media (width<=720px){.setup-checklist li{grid-template-columns:28px minmax(0,1fr)}.setup-actions{grid-column:2;justify-content:flex-start}}.provider-group{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--white-3);margin:8px 0 16px;padding:0 12px}.provider-group>.need:first-of-type{border-top:1px solid var(--white-8)}.provider-group-heading{grid-template-columns:40px minmax(0,1fr) auto;align-items:center;gap:16px;padding:14px 4px;display:grid}.provider-group-heading .connection-name{font-family:var(--font-sans);font-size:var(--text-md)}.provider-section-label{border-top:1px solid var(--white-8);color:var(--muted);font-size:var(--text-xs);letter-spacing:.02em;text-transform:uppercase;margin:0;padding:14px 4px 6px;font-weight:600}.provider-section-label+.need{border-top:0}.provider-group-hint{color:var(--muted);font-size:var(--text-xs);margin:0 4px 14px 60px}@media (width<=720px){.provider-group-heading{grid-template-columns:36px minmax(0,1fr)}.provider-group-hint{margin-left:0}}.agent-dialog.p-dialog{border:1px solid var(--border);border-radius:var(--radius-lg);width:min(600px,100vw - 32px);max-height:calc(100dvh - 48px);color:var(--ink);background:var(--night-900);box-shadow:0 32px 100px #0009}.agent-dialog .p-dialog-header{padding:24px 28px 8px}.agent-dialog .p-dialog-title{font-size:var(--text-xl);letter-spacing:-.025em;font-weight:600}.agent-dialog .p-dialog-content{padding:0 28px 24px}.agent-lead{color:var(--muted);font-size:var(--text-sm);margin:8px 0 24px;line-height:1.6}.agent-fields,.agent-result{gap:16px;margin-top:20px;display:grid}.agent-field{min-width:0;font-size:var(--text-sm);gap:8px;font-weight:500;display:grid}.agent-field .p-inputtext,.agent-field .p-select{width:100%;min-width:0;font-size:var(--text-sm);color:var(--ink);background:var(--field);border-color:var(--field-border)}.agent-field .p-inputtext{padding:10px 12px}.agent-options{grid-template-columns:minmax(0,1fr) 140px;gap:12px;display:grid}.agent-field .p-select-label{white-space:normal}.agent-workspace{color:var(--muted);font-size:var(--text-xs);justify-content:space-between;gap:12px;margin:0;display:flex}.agent-workspace strong{color:var(--ink);overflow-wrap:anywhere}.agent-hint{color:var(--muted);font-size:var(--text-xs);margin:0;line-height:1.6}.agent-success{color:var(--mint);font-size:var(--text-sm);align-items:center;gap:8px;display:flex}.agent-instruction{font-size:var(--text-sm);margin:0;line-height:1.6}.agent-code{border:1px solid var(--border);border-radius:var(--radius-md);color:var(--ink);background:var(--night-950);white-space:pre-wrap;overflow-wrap:anywhere;font-size:var(--text-xs);margin:0;padding:16px;line-height:1.7}.agent-code:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.agent-result>.p-button{justify-self:start}.agent-prompt{color:var(--ink)}.agent-help{border-top:1px solid var(--border);color:var(--subtle);font-size:var(--text-xs);flex-wrap:wrap;justify-content:space-between;gap:12px;margin-top:24px;padding-top:16px;line-height:1.6;display:flex}.agent-help>span{overflow-wrap:anywhere;min-width:0}.agent-help a{color:var(--muted);align-items:center;gap:4px;display:inline-flex}.agent-card{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--white-3);align-items:center;gap:12px;margin-bottom:16px;padding:16px 20px;display:flex}.agent-card>.app-icon{color:var(--muted);flex:none;font-size:20px}.agent-card>div{flex:1;gap:4px;margin-left:4px;display:grid}.agent-card strong{font-size:var(--text-sm);font-weight:500}.agent-card span{color:var(--muted);font-size:var(--text-xs);line-height:1.6}.agent-card>.p-button{flex:none}.setup-copy .setup-hint .link-button{color:var(--muted);font-size:var(--text-xs)}@media (width<=600px){.agent-dialog .p-dialog-header{padding:20px 20px 8px}.agent-dialog .p-dialog-content{padding:0 20px 20px}.agent-options{grid-template-columns:minmax(0,1fr)}.agent-card{flex-wrap:wrap}.agent-card>div{flex-basis:calc(100% - 40px)}}.github-token-guide{color:var(--muted);font-size:var(--text-sm);line-height:1.6}.github-token-guide ol{margin:4px 0;padding-left:20px}.github-token-guide li+li{margin-top:8px}.github-token-guide p{margin:12px 0 0}.github-token-guide a{color:var(--ink);text-decoration:underline}.setup-copy .setup-problem{color:var(--ink);overflow-wrap:anywhere;margin-top:8px}.setup-copy .setup-problem.error{color:var(--danger)}.add-repository{gap:20px;padding:20px;display:grid}.add-repository-source{justify-self:start}.add-repository-source button{align-items:center;gap:8px;display:inline-flex}.add-repository h2{font-size:var(--text-lg);margin:0}.add-repository-local{justify-items:start;gap:12px;max-width:720px;display:grid}.add-repository-connect{gap:12px;max-width:720px;display:grid}.add-repository-connect>.link-button{color:var(--muted);font-size:var(--text-xs);justify-self:start}.add-repository-option{border:1px solid var(--border);border-radius:var(--radius-md);grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:16px;padding:16px;display:grid}.add-repository-option.open{grid-template-columns:minmax(0,1fr)}.add-repository-option>div:first-child{gap:4px;display:grid}.add-repository-option strong{font-size:var(--text-sm);align-items:center;gap:8px;display:inline-flex}.add-repository-option>div:first-child>span{color:var(--muted);font-size:var(--text-xs)}.add-repository-problem{border:1px solid color-mix(in srgb, var(--danger) 35%, transparent);border-radius:var(--radius-md);grid-template-columns:20px minmax(0,1fr) auto;align-items:center;gap:12px 16px;padding:16px;display:grid}.add-repository-problem>.app-icon{color:var(--danger)}.add-repository-problem>div:nth-child(2){gap:4px;min-width:0;display:grid}.add-repository-problem strong{font-size:var(--text-sm)}.add-repository-problem>div>span{color:var(--muted);font-size:var(--text-xs);overflow-wrap:anywhere}.add-repository-problem-actions{flex-wrap:wrap;gap:8px;display:flex}.add-repository-connect p,.add-repository-local p{color:var(--muted);font-size:var(--text-sm);margin:0}.add-repository-connect .github-token-form{width:100%}.add-repository-github{gap:12px;display:grid}.add-repository-list{border:1px solid var(--border);border-radius:var(--radius-md)}.add-repository-row{grid-template-columns:20px minmax(0,1fr) auto auto;grid-template-areas:"icon name branch import";align-items:center;gap:4px 16px;padding:12px 16px;display:grid}.add-repository-glyph{grid-area:icon}.add-repository-name{grid-area:name}.add-repository-other,.add-repository-branch{grid-area:branch}.add-repository-import{grid-area:import;justify-content:center;min-width:72px}.add-repository-row .add-repository-other{color:var(--muted);font-size:var(--text-xs)}.add-repository-row+.add-repository-row{border-top:1px solid var(--white-8)}.add-repository-glyph{color:var(--subtle)}.add-repository-name{min-width:0;display:grid}.add-repository-name strong{font-size:var(--text-sm);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.add-repository-name span{color:var(--subtle);font-size:var(--text-xs)}.add-repository-branch{width:200px}.add-repository-footer{border-top:1px solid var(--border);color:var(--subtle);font-size:var(--text-xs);flex-wrap:wrap;justify-content:space-between;gap:12px;padding-top:16px;display:flex}.add-repository-links{flex-wrap:wrap;align-items:center;gap:16px;display:flex}.add-repository-links a{color:var(--muted);align-items:center;gap:4px;display:inline-flex}.add-repository-path{gap:8px;width:100%;display:flex}.add-repository-path .p-inputtext{flex:1;min-width:0}@media (width<=700px){.add-repository-row{grid-template-columns:20px minmax(0,1fr) auto;grid-template-areas:"icon name import"".branch branch"}.add-repository-other,.add-repository-branch{justify-self:start}.add-repository-branch{width:100%}.add-repository-path{flex-wrap:wrap}.add-repository-option,.add-repository-problem{grid-template-columns:minmax(0,1fr)}.add-repository-problem>.app-icon{display:none}}.shift-status[data-v-a1f4eaea]{min-height:32px;padding:0 var(--row-inset);font-size:var(--text-sm);flex-wrap:wrap;align-items:center;gap:2px 14px;display:flex}.shift-activity[data-v-a1f4eaea]{min-height:32px;color:var(--ink);white-space:nowrap;border-radius:var(--radius-sm);align-items:center;gap:6px;display:inline-flex}.shift-activity[data-v-a1f4eaea]:hover{color:var(--ink);text-decoration:underline}.shift-activity[data-v-a1f4eaea]:focus-visible{outline:2px solid var(--focus);outline-offset:2px}.shift-activity .shift-dot[data-v-a1f4eaea]{background:var(--subtle);border-radius:50%;flex:none;width:6px;height:6px;margin:0 3px}.shift-activity.live .shift-dot[data-v-a1f4eaea]{background:var(--ink);animation:1.6s ease-in-out infinite live-pulse}.shift-activity .app-icon[data-v-a1f4eaea]{width:12px;height:12px;color:var(--subtle)}.shift-activity.idle[data-v-a1f4eaea]{color:var(--muted)}@media (prefers-reduced-motion:reduce){.shift-activity.live .shift-dot[data-v-a1f4eaea]{animation:none}}.local-directory-picker.p-dialog{border:1px solid var(--border);border-radius:var(--radius-lg);background:var(--night-900);width:min(600px,100vw - 32px);max-height:calc(100dvh - 32px);color:var(--ink);box-shadow:0 28px 90px #00000080}.local-directory-picker .p-dialog-header{padding:24px 24px 8px}.local-directory-picker .p-dialog-title{font-family:var(--font-sans);font-size:var(--text-lg);letter-spacing:-.025em}.local-directory-picker .p-dialog-content{padding:0 24px 20px}.local-directory-picker .directory-description{color:var(--muted);font-size:var(--text-xs);margin:0 0 20px;line-height:1.6}.local-directory-picker .directory-location{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-950);align-items:center;gap:8px;margin-bottom:12px;padding:8px;display:flex}.local-directory-picker .directory-location>div{gap:4px;min-width:0;display:grid}.local-directory-picker .directory-location .p-button{flex-shrink:0;width:32px;height:32px}.local-directory-picker .directory-label{color:var(--subtle);font-family:var(--font-mono);font-size:var(--text-xs)}.local-directory-picker code{color:var(--ink);font-size:var(--text-xs);overflow-wrap:anywhere}.local-directory-picker .directory-list{border:1px solid var(--border);border-radius:var(--radius-md);scrollbar-width:thin;scrollbar-color:var(--night-750) transparent;background:#00000073;height:min(360px,44vh);padding:4px;overflow-y:auto}.local-directory-picker .directory-row{border-radius:var(--radius-sm);width:100%;min-height:42px;color:var(--ink);text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;align-items:center;gap:12px;padding:8px 12px;display:flex}.local-directory-picker .directory-row:hover{background:var(--white-8)}.local-directory-picker .directory-row:focus-visible{outline:2px solid var(--focus);outline-offset:-2px;background:var(--white-8)}.local-directory-picker .directory-row>span{overflow-wrap:anywhere;min-width:0;font-size:var(--text-xs);flex:1}.local-directory-picker .directory-icon{color:var(--muted);font-size:16px}.local-directory-picker .directory-chevron{color:var(--subtle);font-size:11px}.local-directory-picker .directory-message{min-height:100%;color:var(--muted);font-size:var(--text-xs);text-align:center;justify-content:center;align-items:center;gap:8px;margin:0;padding:24px;line-height:1.7;display:flex}.local-directory-picker .directory-error{color:var(--danger);font-size:var(--text-xs);line-height:1.6}.local-directory-picker .p-dialog-footer{border-top:1px solid var(--border);gap:8px;padding:16px 24px}.local-directory-picker .directory-count{color:var(--subtle);font-size:var(--text-xs);margin-right:auto}@media (width<=480px){.local-directory-picker .p-dialog-header{padding:16px 16px 8px}.local-directory-picker .p-dialog-content{padding:0 16px 16px}.local-directory-picker .p-dialog-footer{padding:16px}.local-directory-picker .directory-count{display:none}}.oauth-scope-warning[data-v-9deaa9d9]{border:1px solid color-mix(in srgb, var(--amber) 35%, transparent);border-radius:var(--radius-md);background:var(--night-900);font-size:var(--text-xs);align-items:flex-start;gap:12px;margin:0 4px 16px 52px;padding:16px;line-height:1.6;display:flex}.oauth-scope-warning>.app-icon[data-v-9deaa9d9]{color:var(--amber);flex-shrink:0;margin-top:2px}strong[data-v-9deaa9d9]{color:var(--amber);font-size:var(--text-sm);font-weight:600}p[data-v-9deaa9d9]{color:var(--muted);margin:6px 0 0}dl[data-v-9deaa9d9]{gap:10px;margin:14px 0;display:grid}dl>div[data-v-9deaa9d9]{grid-template-columns:80px minmax(0,1fr);gap:12px;display:grid}dt[data-v-9deaa9d9]{color:var(--subtle)}dd[data-v-9deaa9d9]{min-width:0;color:var(--muted);flex-wrap:wrap;gap:6px;margin:0;display:flex}code[data-v-9deaa9d9]{border:1px solid var(--border);border-radius:var(--radius-sm);color:var(--ink);font-size:var(--text-xs);overflow-wrap:anywhere;padding:1px 6px}.scope-guidance[data-v-9deaa9d9]{font-size:var(--text-xs)}@media (width<=700px){.oauth-scope-warning[data-v-9deaa9d9]{margin-left:0}dl>div[data-v-9deaa9d9]{grid-template-columns:1fr;gap:4px}}.model-picker.p-autocomplete{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--white-6);width:100%;min-width:0;min-height:42px;color:var(--ink);text-transform:none;letter-spacing:normal;font-size:var(--text-xs);align-items:stretch}.model-picker.p-autocomplete:hover{border-color:var(--ink)}.model-picker.p-autocomplete:focus-within{border-color:var(--ink);box-shadow:0 0 0 2px var(--white-18)}.need-setup .model-picker .p-autocomplete-input,.model-picker .p-autocomplete-input-multiple{width:0;min-width:0;min-height:40px;box-shadow:none;color:var(--ink);font:inherit;background:0 0;border:0;border-radius:8px 0 0 8px;outline:none;flex:1;margin:0;padding:8px 12px}.model-picker .p-autocomplete-input-multiple{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.model-picker .p-autocomplete-input-multiple input{width:100%;min-width:80px;color:var(--ink);font:inherit;background:0 0;padding:0}.model-picker input::placeholder{color:var(--muted);opacity:1}.model-picker .p-autocomplete-input-chip{flex:1;min-width:80px}.model-picker .p-chip{border-radius:var(--radius-sm);max-width:100%;color:var(--ink);background:var(--white-12);font-size:var(--text-xs);padding:4px 8px;line-height:18px}.model-picker .p-chip-label{overflow-wrap:anywhere}.model-picker .p-autocomplete-dropdown{width:36px;color:var(--muted);background:0 0;border:0;border-radius:0 8px 8px 0}.model-picker .p-autocomplete-dropdown:hover{background:var(--white-12);color:var(--ink)}.model-picker-overlay.p-autocomplete-overlay{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-800);color:var(--ink);box-shadow:var(--shadow);text-transform:none;letter-spacing:normal}.model-picker-overlay .p-autocomplete-option{min-height:40px;color:var(--ink);font-size:var(--text-xs);padding:8px 12px}.model-picker-overlay .p-autocomplete-option:hover,.model-picker-overlay .p-autocomplete-option.p-focus,.model-picker-overlay .p-autocomplete-option-selected{background:var(--white-12);color:var(--ink)}.model-picker-overlay .p-autocomplete-empty-message{color:var(--muted);font-size:var(--text-xs);padding:12px}.model-field[data-v-3ffc7432]{align-content:start;gap:8px;min-width:0;display:grid}.model-field small[data-v-3ffc7432]{color:var(--muted);font-size:var(--text-xs);line-height:1.5}.model-field small.field-error[data-v-3ffc7432]{color:var(--danger)}.model-discovery[data-v-3ffc7432]{border-top:1px solid var(--border);flex-wrap:wrap;align-items:center;gap:12px;padding:16px 0;display:flex}.model-discovery .field-error[data-v-3ffc7432]{color:var(--danger);font-size:var(--text-xs);flex-basis:100%;margin:0}.model-start[data-v-180ea467]{margin-bottom:16px;padding:20px}.model-start-options[data-v-180ea467]{grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px;margin-top:16px;display:grid}.model-start-options button[data-v-180ea467]{border:1px solid var(--border);border-radius:var(--radius-md);color:var(--ink);text-align:left;cursor:pointer;background:0 0;align-content:start;justify-items:start;gap:8px;padding:16px;display:grid}.model-start-options button[data-v-180ea467]:hover:not(:disabled){border-color:var(--ink)}.model-start-options button[data-v-180ea467]:disabled{cursor:not-allowed;opacity:.55}.model-start-options strong[data-v-180ea467]{font-size:var(--text-md);font-weight:600}.model-start-options span[data-v-180ea467]{color:var(--muted);font-size:var(--text-xs);line-height:1.6}.model-start-note[data-v-180ea467],.model-note[data-v-180ea467]{color:var(--muted);font-size:var(--text-xs);margin:12px 0 0;line-height:1.6}.model-note summary[data-v-180ea467]{cursor:pointer}.run-limit[data-v-180ea467]{border-left:1px solid var(--border)}.run-limit label[data-v-180ea467]{align-items:center;gap:8px;display:inline-flex}.run-limit .p-inputtext[data-v-180ea467]{width:6em}.run-limit small[data-v-180ea467]{color:var(--subtle);flex-basis:100%}.run-limit small[role=alert][data-v-180ea467]{color:var(--danger)}.run-row[data-v-875c622e]{min-width:0}.run-links>a[data-v-875c622e]{align-items:center;gap:4px;min-width:0;max-width:100%;display:flex}.run-links strong[data-v-875c622e],.run-links>a>span[data-v-875c622e]{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.run-links .app-icon[data-v-875c622e]{flex:none}.run-links>a:hover strong[data-v-875c622e],.run-links>a:hover>span[data-v-875c622e]{text-underline-offset:3px;text-decoration:underline}.run-links .run-failure[data-v-875c622e]{margin-top:2px}.run-links .run-failure .failure-label[data-v-875c622e]{color:var(--danger)}.run-links .run-failure.canceled .failure-label[data-v-875c622e]{color:var(--muted)}.run-row.compact[data-v-875c622e]{grid-template-columns:52px minmax(0,1fr) 104px 96px}.run-row.compact>.run-when[data-v-875c622e]{display:block}@container (width<=1024px) and (width>=641px){.run-row[data-v-875c622e]:not(.without-job,.compact){grid-template-columns:44px minmax(100px,1fr) 88px minmax(130px,1fr) 168px}.run-row:not(.without-job,.compact)>.run-when[data-v-875c622e]{display:none}.run-row .run-stats[data-v-875c622e],.run-row .run-row-actions[data-v-875c622e]{grid-column:auto}.run-row .run-row-actions[data-v-875c622e]{justify-content:flex-end}}@container (width<=640px){.run-row[data-v-875c622e]:not(.without-job){grid-template-columns:36px minmax(0,1fr) 88px;gap:8px 12px}.run-row:not(.without-job)>.run-when[data-v-875c622e],.run-row.compact>.run-when[data-v-875c622e]{display:none}.run-row .run-stats[data-v-875c622e],.run-row .run-row-actions[data-v-875c622e]{grid-column:2/-1}.run-row .run-row-actions[data-v-875c622e]{justify-content:flex-start}.run-row .row-chevron[data-v-875c622e]{display:none}}.recent-runs-panel[data-v-d8559045]{padding:0;overflow:hidden}.recent-runs-panel>.panel-heading[data-v-d8559045]{margin:0;padding:20px 20px 16px}.activity-main>strong[data-v-d8559045]{align-items:center;gap:6px;min-width:0;display:flex}.external-skill[data-v-de652268]{gap:16px;min-width:0;display:grid}.external-skill-grid[data-v-de652268]{grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);gap:12px;display:grid}.external-skill-grid[data-v-de652268] input,.external-skill[data-v-de652268] #external-skill-repository{font-family:var(--font-mono);font-size:var(--text-xs)}.external-skill-check[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);align-items:center;gap:12px;line-height:1.5;display:flex}.external-skill-check .p-button[data-v-de652268]{flex:none}.external-skill-preview[data-v-de652268]{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-950);gap:12px;padding:16px;display:grid}.external-skill-heading[data-v-de652268]{justify-content:space-between;align-items:flex-start;gap:12px;display:flex}.external-skill-heading strong[data-v-de652268]{font-size:var(--text-sm)}.external-skill-heading p[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);margin:4px 0 0;line-height:1.5}.external-skill-heading .detail-link[data-v-de652268]{font-size:var(--text-xs);flex:none;align-items:center;gap:4px;display:inline-flex}.external-skill-meta[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);overflow-wrap:anywhere;align-items:center;gap:6px;margin:0;display:flex}.external-skill-meta .app-icon[data-v-de652268]{flex:none}.external-skill-preview h3[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);margin:4px 0 0;font-weight:500}.external-skill-needs[data-v-de652268]{gap:8px;margin:0;padding:0;list-style:none;display:grid}.external-skill-needs li[data-v-de652268]{border:1px solid var(--border);border-radius:var(--radius-sm);grid-template-columns:minmax(0,1fr) auto;gap:4px 12px;padding:10px 12px;display:grid}.external-skill-need-name[data-v-de652268]{min-width:0;font-size:var(--text-xs);flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.external-skill-need-name>span[data-v-de652268]{color:var(--muted);font-family:var(--font-mono);overflow-wrap:anywhere}.external-skill-needs small[data-v-de652268]{color:var(--subtle);font-size:var(--text-xs);grid-column:1/-1;line-height:1.5}.external-skill-status[data-v-de652268]{border:1px solid var(--border);color:var(--muted);font-size:var(--text-xs);white-space:nowrap;border-radius:999px;align-self:start;padding:2px 8px}.external-skill-status.ready[data-v-de652268]{border-color:color-mix(in srgb, var(--mint) 45%, transparent);color:var(--mint)}.external-skill-status.needs_approval[data-v-de652268]{border-color:color-mix(in srgb, var(--amber) 45%, transparent);color:var(--amber)}.external-skill-status.unavailable[data-v-de652268]{border-color:color-mix(in srgb, var(--danger) 45%, transparent);color:var(--danger)}.external-skill-none[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);margin:0;line-height:1.5}.external-skill-text summary[data-v-de652268]{color:var(--muted);font-size:var(--text-xs);cursor:pointer;align-items:center;gap:6px;list-style:none;display:flex}.external-skill-text summary .app-icon[data-v-de652268]{transition:transform .15s}.external-skill-text[open] summary .app-icon[data-v-de652268]{transform:rotate(90deg)}.external-skill-text pre[data-v-de652268]{border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--night-900);max-height:280px;font-family:var(--font-mono);font-size:var(--text-xs);white-space:pre-wrap;overflow-wrap:anywhere;margin:8px 0 0;padding:12px;line-height:1.5;overflow:auto}.external-skill-text small[data-v-de652268]{color:var(--subtle);font-size:var(--text-xs);margin-top:6px;display:block}.external-skill-note[data-v-de652268]{color:var(--subtle);font-size:var(--text-xs);align-items:flex-start;gap:8px;margin:0;line-height:1.5;display:flex}.external-skill-note .app-icon[data-v-de652268]{flex:none;margin-top:2px}@media (width<=540px){.external-skill-grid[data-v-de652268]{grid-template-columns:minmax(0,1fr)}.external-skill-check[data-v-de652268]{flex-wrap:wrap}}.task-composer.p-dialog{border:1px solid var(--border);border-radius:var(--radius-lg);width:min(660px,100vw - 32px);max-height:calc(100dvh - 48px);color:var(--ink);background:var(--night-900);box-shadow:0 32px 100px #0009}.task-composer .p-dialog-header{padding:24px 28px 8px}.task-composer .p-dialog-title{font-size:var(--text-xl);letter-spacing:-.025em;font-weight:600}.task-composer .p-dialog-content{padding:0 28px 24px}.task-composer .p-dialog-footer{border-top:1px solid var(--border);background:var(--night-850);border-radius:0 0 16px 16px;flex-wrap:wrap;gap:8px;padding:16px 28px}.task-composer .p-dialog-footer .p-button{white-space:nowrap}.task-context{color:var(--muted);font-size:var(--text-xs);align-items:center;gap:8px;display:flex}.task-context>span:first-of-type{text-overflow:ellipsis;white-space:nowrap;max-width:65%;overflow:hidden}.task-context .app-icon{flex:none;font-size:14px}.task-context-separator{color:var(--subtle)}.task-lead{color:var(--muted);font-size:var(--text-sm);margin:16px 0 24px;line-height:1.6}.task-guide-link{color:var(--ink);text-underline-offset:3px;align-items:center;gap:3px;text-decoration:underline;display:inline-flex}.task-fields{border:0;grid-template-columns:minmax(0,1fr);gap:24px;min-width:0;margin:0;padding:0;display:grid}.task-field{align-content:start;gap:8px;min-width:0;display:grid}.task-field>label{color:var(--ink);font-size:var(--text-sm);align-items:center;gap:8px;font-weight:500;display:flex}.task-field>label>span{color:var(--subtle);font-size:var(--text-xs);font-weight:400}.task-field small{color:var(--muted);font-size:var(--text-xs);line-height:1.5}.task-field .p-inputtext,.task-field .p-textarea{width:100%;min-width:0;font-family:var(--font-sans);font-size:var(--text-sm);color:var(--ink);background:var(--night-950);border:1px solid var(--field-border);border-radius:var(--radius-md);padding:10px 12px;line-height:1.6}.task-field .p-textarea{resize:vertical;min-height:124px;max-height:320px}.task-field .p-inputtext::placeholder,.task-field .p-textarea::placeholder{color:var(--subtle)}.task-field .p-inputtext:focus,.task-field .p-textarea:focus{border-color:var(--focus);outline:2px solid var(--white-8);outline-offset:1px}.task-skill-picker{width:100%;display:flex}.task-skill-picker:has(.p-autocomplete-dropdown) .p-inputtext{border-radius:8px 0 0 8px}.task-skill-picker .p-autocomplete-dropdown{border:1px solid var(--field-border);background:var(--raised);width:40px;color:var(--muted);border-left:0;border-radius:0 8px 8px 0;flex:none}.task-skill-overlay.p-autocomplete-overlay{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-850);max-width:calc(100vw - 40px);color:var(--ink);box-shadow:0 12px 32px #0006}.task-skill-overlay .p-autocomplete-option{color:var(--ink);font-family:var(--font-mono);font-size:var(--text-xs);white-space:normal;overflow-wrap:anywhere;padding:10px 12px}.task-skill-overlay .p-autocomplete-option.p-focus,.task-skill-overlay .p-autocomplete-option:hover{background:var(--white-6)}.task-skill-picker .p-inputtext,.task-field .task-skill-path{font-family:var(--font-mono);font-size:var(--text-xs)}.task-options{border-top:1px solid var(--border);border-bottom:1px solid var(--border);min-width:0}.task-option+.task-option{border-top:1px solid var(--border)}.task-option>summary{min-height:46px;color:var(--muted);font-size:var(--text-xs);cursor:pointer;align-items:center;gap:8px;padding:10px 0;list-style:none;display:flex}.task-option>summary::-webkit-details-marker{display:none}.task-option>summary:hover{color:var(--ink)}.task-option>summary:focus-visible{outline:2px solid var(--focus);outline-offset:3px;border-radius:var(--radius-sm)}.task-option>summary .app-icon{font-size:14px;transition:transform .15s}.task-option[open]>summary .app-icon{transform:rotate(90deg)}.task-option-value{text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:48%;color:var(--subtle);font-size:var(--text-xs);margin-left:auto;overflow:hidden}.task-settings-grid{grid-template-columns:1fr 1fr;gap:20px;padding:8px 0 20px;display:grid}.task-start-field{grid-column:1/-1}.task-start-field input{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}.task-access{padding:4px 0 16px}.task-access>p{color:var(--muted);font-size:var(--text-xs);margin:0 0 12px;line-height:1.6}.task-connection{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-950);margin-top:12px;padding:12px}.task-connection-heading{font-size:var(--text-xs);align-items:center;gap:8px;margin-bottom:8px;display:flex}.task-connection-heading .provider-icon{font-size:16px}.task-connection-heading strong{font-weight:500}.task-connection-heading>span:last-child{color:var(--subtle);font-size:var(--text-xs);margin-left:auto}.task-tool-grid{grid-template-columns:repeat(2,minmax(0,1fr));gap:4px 12px;display:grid}.task-choice{border-radius:var(--radius-sm);min-width:0;min-height:36px;color:var(--muted);cursor:pointer;align-items:center;gap:8px;padding:6px;display:flex}.task-choice:hover{color:var(--ink);background:var(--white-6)}.task-choice:has(input:checked){color:var(--ink);background:var(--white-3)}.task-choice input{width:15px;height:15px;accent-color:var(--ink);flex:none;margin:0}.task-choice span{overflow-wrap:anywhere;min-width:0;font-family:var(--font-mono);font-size:var(--text-xs)}.task-delivery{margin-top:20px}.task-delivery h3{color:var(--muted);font-size:var(--text-xs);margin:0 0 8px;font-weight:500}.task-delivery .task-choice span{font-family:var(--font-sans);font-size:var(--text-xs)}.task-access .task-access-note{color:var(--subtle);font-size:var(--text-xs);margin:14px 0 0}.task-footer-note{color:var(--subtle);font-size:var(--text-xs);align-items:center;gap:6px;margin-right:auto;display:flex}.task-footer-note .app-icon{font-size:14px}.task-error{border:1px solid var(--border);border-radius:var(--radius-md);color:var(--danger);font-size:var(--text-xs);align-items:flex-start;gap:8px;margin:0;padding:12px;line-height:1.5;display:flex}.task-error .app-icon{flex:none;margin-top:2px}@media (width<=540px){.task-composer.p-dialog{border-radius:var(--radius-lg);width:calc(100vw - 24px);max-height:calc(100dvh - 24px)}.task-composer .p-dialog-header{padding:20px 20px 8px}.task-composer .p-dialog-content{padding:0 20px 20px}.task-composer .p-dialog-footer{padding:14px 20px}.task-settings-grid,.task-tool-grid{grid-template-columns:minmax(0,1fr)}.task-footer-note{display:none}.task-context{flex-wrap:wrap}}.task-source[data-v-9f136c11]{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-950);gap:4px;padding:4px;display:flex}.task-source button[data-v-9f136c11]{border-radius:var(--radius-sm);min-height:36px;color:var(--muted);font-family:var(--font-sans);font-size:var(--text-xs);cursor:pointer;background:0 0;border:1px solid #0000;flex:1;justify-content:center;align-items:center;gap:8px;padding:8px 10px;font-weight:500;display:flex}.task-source button[aria-pressed=true][data-v-9f136c11]{border-color:var(--raised-border);color:var(--ink);background:var(--raised);box-shadow:var(--raised-shadow)}.task-source button[data-v-9f136c11]:focus-visible{outline:2px solid var(--focus);outline-offset:1px}.task-source .app-icon[data-v-9f136c11]{flex:none;font-size:15px}.repository-page[data-v-1bb2e5e3] .page-actions{flex-wrap:wrap;flex:none;align-items:center}.repository-page[data-v-1bb2e5e3] .page-actions .p-button{white-space:nowrap}.run-access-needs[data-v-ace8e8a8]{gap:10px;margin:0;padding:0;list-style:none;display:grid}.run-access-needs li[data-v-ace8e8a8]{border:1px solid var(--border);border-radius:var(--radius-sm);font-size:var(--text-sm);gap:4px;padding:10px 12px;display:grid}.run-access-service[data-v-ace8e8a8]{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.run-access-service code[data-v-ace8e8a8]{color:var(--muted);font-size:var(--text-xs);overflow-wrap:anywhere}.run-access-next[data-v-ace8e8a8]{font-size:var(--text-xs);margin:12px 0 0}.schedule-skill-yaml[data-v-ace8e8a8]{white-space:pre;margin:12px 0 0;overflow-x:auto}.run-queue-help[data-v-ace8e8a8]{color:var(--muted);font-size:var(--text-xs);margin:12px 0 0}.run-queue-help a[data-v-ace8e8a8]{text-underline-offset:3px;align-items:center;gap:4px;text-decoration:underline;display:inline-flex}.run-detail-page[data-v-ace8e8a8] .page-header>div:first-child{min-width:0;max-width:100%}.run-detail-page[data-v-ace8e8a8] .page-header p{overflow-wrap:anywhere}.task-input-panel[data-v-ace8e8a8]{padding:20px}.task-input-panel>summary[data-v-ace8e8a8]{color:var(--muted);font-size:var(--text-md);cursor:pointer;justify-content:space-between;align-items:center;gap:12px;font-weight:500;list-style:none;display:flex}.task-input-panel>summary[data-v-ace8e8a8]::-webkit-details-marker{display:none}.task-input-panel>summary[data-v-ace8e8a8]:focus-visible{outline:2px solid var(--focus);outline-offset:6px;border-radius:var(--radius-sm)}.task-input-panel .app-icon[data-v-ace8e8a8]{flex:none;font-size:14px}.task-input-panel[open]>summary[data-v-ace8e8a8]{color:var(--ink)}.task-input-panel[open] .app-icon[data-v-ace8e8a8]{transform:rotate(90deg)}.task-instructions-meta[data-v-ace8e8a8]{color:var(--subtle);font-size:var(--text-xs);align-items:center;gap:12px;font-weight:400;display:flex}.task-instructions[data-v-ace8e8a8]{border-top:1px solid var(--border);color:var(--muted);font-size:var(--text-sm);overflow-wrap:anywhere;margin-top:18px;padding-top:16px;line-height:1.75}.task-instructions[data-v-ace8e8a8]>:first-child{margin-top:0}.task-instructions[data-v-ace8e8a8]>:last-child{margin-bottom:0}.task-instructions[data-v-ace8e8a8] h1,.task-instructions[data-v-ace8e8a8] h2,.task-instructions[data-v-ace8e8a8] h3{color:var(--ink);font-size:var(--text-md);font-weight:500}.run-files-panel[data-v-ace8e8a8]{gap:12px;display:grid}.run-file-line[data-v-ace8e8a8]{font-size:var(--text-sm);overflow-wrap:anywhere;justify-content:space-between;align-items:baseline;gap:16px;display:flex}.run-saved-files[data-v-ace8e8a8]{border-top:1px solid var(--border);gap:8px;margin:0;padding:12px 0 0;list-style:none;display:grid}.run-files-changes+.run-saved-files[data-v-ace8e8a8]{margin-top:4px}.run-changed-files[data-v-ace8e8a8]{font-size:var(--text-xs);margin-top:8px}.run-changed-files summary[data-v-ace8e8a8]{color:var(--muted);cursor:pointer}.run-changed-files ul[data-v-ace8e8a8]{gap:4px;margin:8px 0 0;padding-left:16px;display:grid}.run-skipped-file[data-v-ace8e8a8]{font-size:var(--text-xs);margin:0}.run-delivery+.run-delivery[data-v-ace8e8a8]{margin-top:6px}.run-delivery-status.delivered[data-v-ace8e8a8]{color:var(--mint)}.run-delivery-status.failed[data-v-ace8e8a8]{color:var(--danger)}.run-delivery-status.retrying[data-v-ace8e8a8]{color:var(--amber)}.run-delivery-status.pending[data-v-ace8e8a8],.run-delivery-status.skipped[data-v-ace8e8a8]{color:var(--subtle)}.run-delivery-reason[data-v-ace8e8a8]{font-size:var(--text-xs);display:block}.run-delivery-link[data-v-ace8e8a8]{font-size:var(--text-xs);color:var(--ink);text-underline-offset:3px;align-items:center;gap:4px;margin-top:2px;text-decoration:underline;display:inline-flex}.run-delivery-link .app-icon[data-v-ace8e8a8]{font-size:12px}.run-delivery-retry[data-v-ace8e8a8]{margin-top:6px}.run-delivery-issues[data-v-ace8e8a8]{font-size:var(--text-xs);margin:4px 0 0;padding:0;list-style:none}.run-delivery-issues li+li[data-v-ace8e8a8]{margin-top:2px}@media (width<=540px){.task-input-panel[data-v-ace8e8a8]{padding:16px 20px}}.try-panel[data-v-9698921c]{gap:12px;padding:24px;display:grid}.try-panel h2[data-v-9698921c]{font-size:var(--text-md);margin:0;font-weight:600}.try-help[data-v-9698921c]{color:var(--muted);font-size:var(--text-sm);margin:0}.try-skill[data-v-9698921c]{color:var(--muted);font-size:var(--text-xs);overflow-wrap:anywhere;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 8px;display:flex}.try-repositories[data-v-9698921c]{gap:8px;margin:8px 0 0;padding:0;list-style:none;display:grid}.try-repositories button[data-v-9698921c]{border:1px solid var(--border);border-radius:var(--radius-md);background:var(--night-950);width:100%;min-height:48px;color:var(--ink);font:inherit;font-size:var(--text-sm);text-align:left;cursor:pointer;align-items:center;gap:10px;padding:10px 14px;display:flex}.try-repositories button[data-v-9698921c]:hover{border-color:var(--raised-border);background:var(--raised)}.try-repositories button[data-v-9698921c]:focus-visible{outline:2px solid var(--focus);outline-offset:1px}.try-repositories button>span[data-v-9698921c]{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;overflow:hidden}.try-repositories code[data-v-9698921c]{color:var(--subtle);font-size:var(--text-xs)}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
2
|
+
<!-- The mark on a dark tile, so the grey moon shows on light browser tabs too. -->
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="night" x1="8" y1="8" x2="56" y2="56" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop stop-color="#ededed"/>
|
|
6
|
+
<stop offset="1" stop-color="#737373"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="shift" x1="19" y1="42" x2="49" y2="25" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop stop-color="#ffb454"/>
|
|
10
|
+
<stop offset="1" stop-color="#ffd38c"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect width="64" height="64" rx="14" fill="#131313"/>
|
|
14
|
+
<g transform="translate(5 6) scale(.84)">
|
|
15
|
+
<path fill="url(#night)" d="M34 5a27 27 0 1 0 25 37.2A23 23 0 1 1 34 5Z"/>
|
|
16
|
+
<path fill="url(#shift)" d="M18 39h21.2l-5.6 5.6 4.8 4.8L52 35.8 38.4 22.2 33.6 27l5.2 5.2H18V39Z"/>
|
|
17
|
+
<circle cx="48.5" cy="15.5" r="3.5" fill="#ffe3ab"/>
|
|
18
|
+
</g>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" class="secondshift-dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<meta name="theme-color" content="#0d0d0d" />
|
|
7
|
+
<meta name="description" content="Your agents, safely on duty." />
|
|
8
|
+
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg" />
|
|
9
|
+
<title>SecondShift</title>
|
|
10
|
+
<script type="module" crossorigin src="/static/assets/index-Bwy0hvk_.js"></script>
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/static/assets/index-D27gB9_L.css">
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" role="img" aria-labelledby="title">
|
|
2
|
+
<title id="title">SecondShift</title>
|
|
3
|
+
<defs>
|
|
4
|
+
<linearGradient id="night" x1="8" y1="8" x2="56" y2="56" gradientUnits="userSpaceOnUse">
|
|
5
|
+
<stop stop-color="#ededed"/>
|
|
6
|
+
<stop offset="1" stop-color="#737373"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<linearGradient id="shift" x1="19" y1="42" x2="49" y2="25" gradientUnits="userSpaceOnUse">
|
|
9
|
+
<stop stop-color="#ffb454"/>
|
|
10
|
+
<stop offset="1" stop-color="#ffd38c"/>
|
|
11
|
+
</linearGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<path fill="url(#night)" d="M34 5a27 27 0 1 0 25 37.2A23 23 0 1 1 34 5Z"/>
|
|
14
|
+
<path fill="url(#shift)" d="M18 39h21.2l-5.6 5.6 4.8 4.8L52 35.8 38.4 22.2 33.6 27l5.2 5.2H18V39Z"/>
|
|
15
|
+
<circle cx="48.5" cy="15.5" r="3.5" fill="#ffe3ab"/>
|
|
16
|
+
</svg>
|
|
File without changes
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Who may do what in a workspace.
|
|
2
|
+
|
|
3
|
+
Access comes only from WorkspaceMembership. Each request works in one
|
|
4
|
+
workspace: the one the person chose in this session, or else the first one
|
|
5
|
+
they joined. Every check names an Action, and POLICY says which role it needs.
|
|
6
|
+
|
|
7
|
+
A request signed with an API token works in the token's workspace. It can
|
|
8
|
+
take only the actions its scope lists in TOKEN_ACTIONS, and only while its
|
|
9
|
+
creator's role still allows them.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from enum import StrEnum
|
|
13
|
+
|
|
14
|
+
from django.db.models import QuerySet
|
|
15
|
+
from django.http import HttpRequest
|
|
16
|
+
|
|
17
|
+
from secondshift.workspaces.dao import (
|
|
18
|
+
first_membership,
|
|
19
|
+
membership_in,
|
|
20
|
+
memberships_for_user,
|
|
21
|
+
)
|
|
22
|
+
from secondshift.workspaces.models import ApiToken, Workspace, WorkspaceMembership
|
|
23
|
+
|
|
24
|
+
Role = WorkspaceMembership.Role
|
|
25
|
+
|
|
26
|
+
# Higher roles can do everything lower roles can.
|
|
27
|
+
ROLE_RANK = {Role.MEMBER: 1, Role.ADMIN: 2, Role.OWNER: 3}
|
|
28
|
+
# The chosen workspace's id is kept in the session under this key.
|
|
29
|
+
CURRENT_WORKSPACE_SESSION_KEY = "secondshift.workspace"
|
|
30
|
+
# The resolved membership is kept on the request under this name.
|
|
31
|
+
MEMBERSHIP_ATTRIBUTE = "secondshift_membership"
|
|
32
|
+
# The API token that signed the request, if any, is kept under this name.
|
|
33
|
+
API_TOKEN_ATTRIBUTE = "secondshift_api_token"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class Action(StrEnum):
|
|
37
|
+
"""Each value finishes the sentence "Only ... can"."""
|
|
38
|
+
|
|
39
|
+
# Owners: credentials, approvals and the host's files.
|
|
40
|
+
ADD_GITHUB_REPOSITORIES = "add GitHub repositories"
|
|
41
|
+
ADD_LOCAL_REPOSITORIES = "add local repositories"
|
|
42
|
+
VIEW_LOCAL_PATHS = "see local repository paths"
|
|
43
|
+
ADD_CONNECTIONS = "add connections"
|
|
44
|
+
ADD_MCP_CONNECTIONS = "add MCP connections"
|
|
45
|
+
ADD_API_CONNECTIONS = "add API connections"
|
|
46
|
+
ADD_MODEL_PROVIDERS = "add model providers"
|
|
47
|
+
EDIT_CONNECTIONS = "edit connections"
|
|
48
|
+
EDIT_MODEL_PROVIDERS = "edit model providers"
|
|
49
|
+
DISCONNECT_CONNECTIONS = "disconnect connections"
|
|
50
|
+
DELETE_CONNECTIONS = "delete connections"
|
|
51
|
+
TEST_CONNECTIONS = "test connections"
|
|
52
|
+
REVIEW_CAPABILITIES = "review capabilities"
|
|
53
|
+
ALLOW_PULL_REQUESTS = "let tasks open pull requests"
|
|
54
|
+
ALLOW_ISSUES = "let jobs open issues"
|
|
55
|
+
INVITE_PEOPLE = "invite people"
|
|
56
|
+
MANAGE_MEMBERS = "change roles or remove people"
|
|
57
|
+
# Admins: running the work that owners approved.
|
|
58
|
+
RESYNC_REPOSITORIES = "resync repositories"
|
|
59
|
+
CHANGE_REPOSITORY_STATE = "change repository state"
|
|
60
|
+
REMOVE_REPOSITORIES = "remove repositories"
|
|
61
|
+
ENABLE_JOBS = "enable jobs"
|
|
62
|
+
RUN_JOBS = "run jobs"
|
|
63
|
+
CHANGE_RUN_LIMITS = "change run limits"
|
|
64
|
+
MANAGE_TRIGGERS = "turn job triggers on or off"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
ADMIN_ACTIONS = {
|
|
68
|
+
Action.RESYNC_REPOSITORIES,
|
|
69
|
+
Action.CHANGE_REPOSITORY_STATE,
|
|
70
|
+
Action.REMOVE_REPOSITORIES,
|
|
71
|
+
Action.ENABLE_JOBS,
|
|
72
|
+
Action.RUN_JOBS,
|
|
73
|
+
Action.CHANGE_RUN_LIMITS,
|
|
74
|
+
Action.MANAGE_TRIGGERS,
|
|
75
|
+
}
|
|
76
|
+
# The lowest role allowed to take each action. Members can only view.
|
|
77
|
+
POLICY = {
|
|
78
|
+
action: Role.ADMIN if action in ADMIN_ACTIONS else Role.OWNER for action in Action
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# What each API token scope may change. Reading needs no action. Owner
|
|
83
|
+
# actions are never listed, so no token can grant access or touch credentials.
|
|
84
|
+
TOKEN_ACTIONS = {
|
|
85
|
+
ApiToken.Scope.READ: frozenset[Action](),
|
|
86
|
+
ApiToken.Scope.RUN: frozenset({Action.RUN_JOBS, Action.RESYNC_REPOSITORIES}),
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class AccessDenied(Exception):
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def role_allows(role: str | None, action: Action) -> bool:
|
|
95
|
+
if role is None:
|
|
96
|
+
return False
|
|
97
|
+
return ROLE_RANK[Role(role)] >= ROLE_RANK[POLICY[action]]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_operator(request: HttpRequest) -> bool:
|
|
101
|
+
"""Staff run the server. Only they are shown its environment variables."""
|
|
102
|
+
return bool(getattr(request.user, "is_staff", False))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def denial_message(action: Action) -> str:
|
|
106
|
+
if POLICY[action] == Role.OWNER:
|
|
107
|
+
return f"Only workspace owners can {action}."
|
|
108
|
+
return f"Only workspace owners and admins can {action}."
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def user_memberships(request: HttpRequest) -> QuerySet[WorkspaceMembership]:
|
|
112
|
+
"""The signed-in person's memberships, the first one joined first."""
|
|
113
|
+
return memberships_for_user(request.user.pk)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def request_token(request: HttpRequest) -> ApiToken | None:
|
|
117
|
+
return getattr(request, API_TOKEN_ATTRIBUTE, None)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def current_membership(request: HttpRequest) -> WorkspaceMembership | None:
|
|
121
|
+
if hasattr(request, MEMBERSHIP_ATTRIBUTE):
|
|
122
|
+
return getattr(request, MEMBERSHIP_ATTRIBUTE)
|
|
123
|
+
membership = None
|
|
124
|
+
token = request_token(request)
|
|
125
|
+
if token is not None:
|
|
126
|
+
# A token works only in its own workspace, whatever the session says.
|
|
127
|
+
membership = membership_in(request.user.pk, token.workspace_id)
|
|
128
|
+
elif request.user.is_authenticated:
|
|
129
|
+
chosen = request.session.get(CURRENT_WORKSPACE_SESSION_KEY)
|
|
130
|
+
if chosen is not None:
|
|
131
|
+
membership = membership_in(request.user.pk, chosen)
|
|
132
|
+
# A workspace the person has left falls back to their first one.
|
|
133
|
+
membership = membership or first_membership(request.user.pk)
|
|
134
|
+
setattr(request, MEMBERSHIP_ATTRIBUTE, membership)
|
|
135
|
+
return membership
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def current_workspace(request: HttpRequest) -> Workspace | None:
|
|
139
|
+
membership = current_membership(request)
|
|
140
|
+
return membership.workspace if membership else None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def current_role(request: HttpRequest) -> str | None:
|
|
144
|
+
membership = current_membership(request)
|
|
145
|
+
return membership.role if membership else None
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def token_allows(request: HttpRequest, action: Action) -> bool:
|
|
149
|
+
"""True without a token. With one, whether its scope lists the action."""
|
|
150
|
+
token = request_token(request)
|
|
151
|
+
return token is None or action in TOKEN_ACTIONS[ApiToken.Scope(token.scope)]
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
def allows(request: HttpRequest, action: Action) -> bool:
|
|
155
|
+
return token_allows(request, action) and role_allows(current_role(request), action)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def permitted_actions(request: HttpRequest) -> list[Action]:
|
|
159
|
+
return [action for action in Action if allows(request, action)]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def require(request: HttpRequest, action: Action) -> Workspace:
|
|
163
|
+
"""The current workspace, when the person's role allows the action."""
|
|
164
|
+
membership = current_membership(request)
|
|
165
|
+
if membership is None or not allows(request, action):
|
|
166
|
+
if not token_allows(request, action):
|
|
167
|
+
raise AccessDenied(f"This API token cannot {action}.")
|
|
168
|
+
raise AccessDenied(denial_message(action))
|
|
169
|
+
return membership.workspace
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def select_workspace(
|
|
173
|
+
request: HttpRequest, workspace_id: int
|
|
174
|
+
) -> WorkspaceMembership | None:
|
|
175
|
+
"""Work in another of the person's workspaces. None when not a member."""
|
|
176
|
+
membership = membership_in(request.user.pk, workspace_id)
|
|
177
|
+
if membership is None:
|
|
178
|
+
return None
|
|
179
|
+
request.session[CURRENT_WORKSPACE_SESSION_KEY] = workspace_id
|
|
180
|
+
setattr(request, MEMBERSHIP_ATTRIBUTE, membership)
|
|
181
|
+
return membership
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from django.contrib import admin
|
|
2
|
+
from django.http import HttpRequest
|
|
3
|
+
|
|
4
|
+
from secondshift.workspaces.models import ApiToken, Workspace, WorkspaceMembership
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class MembershipInline(admin.TabularInline):
|
|
8
|
+
model = WorkspaceMembership
|
|
9
|
+
extra = 0
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@admin.register(Workspace)
|
|
13
|
+
class WorkspaceAdmin(admin.ModelAdmin):
|
|
14
|
+
list_display = ("name", "slug", "created_at")
|
|
15
|
+
search_fields = ("name", "slug", "memberships__user__email")
|
|
16
|
+
prepopulated_fields = {"slug": ("name",)}
|
|
17
|
+
inlines = [MembershipInline]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@admin.register(WorkspaceMembership)
|
|
21
|
+
class WorkspaceMembershipAdmin(admin.ModelAdmin):
|
|
22
|
+
list_display = ("workspace", "user", "role", "created_at")
|
|
23
|
+
list_filter = ("role",)
|
|
24
|
+
search_fields = ("workspace__name", "user__email")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@admin.register(ApiToken)
|
|
28
|
+
class ApiTokenAdmin(admin.ModelAdmin):
|
|
29
|
+
list_display = ("name", "workspace", "created_by", "scope", "expires_at")
|
|
30
|
+
list_filter = ("scope",)
|
|
31
|
+
search_fields = ("name", "workspace__name", "created_by__email")
|
|
32
|
+
exclude = ("token_hash",)
|
|
33
|
+
readonly_fields = ("last_used_at",)
|
|
34
|
+
|
|
35
|
+
def has_add_permission(self, request: HttpRequest) -> bool:
|
|
36
|
+
# A token's secret is generated and shown once from the dashboard.
|
|
37
|
+
return False
|